From 96369b59d1c437a61d5615a54777bdc3c342d92b Mon Sep 17 00:00:00 2001 From: ADLMeganBohland Date: Thu, 25 Jul 2024 10:38:42 -0400 Subject: [PATCH] Finished updating cmi5_connectors try/catch --- classes/local/au.php | 6 +- classes/local/au_helpers.php | 9 +- classes/local/cmi5_connectors.php | 377 ++++++----- classes/local/errorover.php | 39 +- cmi5PHP/tests/auTest.php | 6 +- cmi5PHP/tests/ausHelpersTest.php | 90 +-- cmi5PHP/tests/cmi5TestHelpers.php | 6 + cmi5PHP/tests/cmi5_connectorsTest.php | 867 +++++++++++++++++++++----- lib.php | 13 +- 9 files changed, 1068 insertions(+), 345 deletions(-) diff --git a/classes/local/au.php b/classes/local/au.php index b9c34f9..b3eb7fe 100644 --- a/classes/local/au.php +++ b/classes/local/au.php @@ -38,10 +38,10 @@ class au { public function __construct($statement) { // What can go wrong here? It could be that a statement is null - // or that the statement is not an array. - if (is_null($statement) || !is_array($statement)) { + // or that the statement is not an array or not an object. + if (is_null($statement) || (!is_array($statement) && !is_object($statement) )) { - throw new nullException('Statement to build AU is null or not an array.', 0); + throw new nullException('Statement to build AU is null or not an array/object.', 0); } // If it is an array, create the object. foreach ($statement as $key => $value) { diff --git a/classes/local/au_helpers.php b/classes/local/au_helpers.php index 82c1545..5fad354 100644 --- a/classes/local/au_helpers.php +++ b/classes/local/au_helpers.php @@ -26,6 +26,7 @@ use mod_cmi5launch\local\au; use mod_cmi5launch\local\errorover; +global $CFG; // Include the errorover (error override) funcs. require_once ($CFG->dirroot . '/mod/cmi5launch/classes/local/errorover.php'); @@ -53,6 +54,8 @@ public function get_cmi5launch_retrieve_aus_from_db() { public function cmi5launch_retrieve_aus($returnedinfo) { $resultchunked = ""; + + // Use our own more specific error handler, to give better info tto user. set_error_handler('mod_cmi5launch\local\array_chunk_warning', E_WARNING); @@ -244,10 +247,12 @@ public function cmi5launch_retrieve_aus_from_db($auid) { $auitem = $DB->get_record('cmi5launch_aus', array('id' => $auid)); $au = new au($auitem); + + // Return our new list of AU. + return $au; } - // Return our new list of AU. - return $au; + } } diff --git a/classes/local/cmi5_connectors.php b/classes/local/cmi5_connectors.php index 439576c..1f3ab33 100755 --- a/classes/local/cmi5_connectors.php +++ b/classes/local/cmi5_connectors.php @@ -25,6 +25,8 @@ defined('MOODLE_INTERNAL') || die(); use mod_cmi5launch\local\cmi5launch_helpers; +// Include the errorover (error override) funcs. +require_once ($CFG->dirroot . '/mod/cmi5launch/classes/local/errorover.php'); class cmi5_connectors { @@ -60,6 +62,10 @@ public function cmi5launch_get_send_request_to_cmi5_player_get() { } + public function cmi5launch_get_connectors_error_message() { + return [$this, 'cmi5launch_connectors_error_message']; + } + /** * Function to create a course. * @param mixed $id - tenant id in Moodle. @@ -81,20 +87,37 @@ public function cmi5launch_create_course($id, $tenanttoken, $filename) { $databody = $filename->get_content(); + // So this one has some troubleshooting built in already, but we probably need to throw an exception to stop function or moodle will freak + // Sends the stream to the specified URL. $result = $this->cmi5launch_send_request_to_cmi5_player_post('cmi5launch_stream_and_send',$databody, $url, $filetype, $tenanttoken); - // Check result and display message if not 200. - $resulttest = $this->cmi5launch_connectors_error_message($result, "creating the course"); - - if ($resulttest == true) { - // Return an array with course info. - return $result; - } else { - return false; + // Now this will never return false, it will throw an exception if it fails, so we can just return the result + try { + // Check result and display message if not 200. + $resulttest = $this->cmi5launch_connectors_error_message($result, "creating the course"); + + // echo "Is it resulting?"; + if ($resulttest == true) { + // Return an array with course info. + return $result; + // I think this is the problem, it is coming back and throwing another error! I thought it would stop... do I need a kill in the error message hander? + // Its throwing BOB??? The third path is executing, THATS the problem! + // either way though, shouldn't the error funtion have ITS own test? like, + // what we need to test here is is resulttrue is true or not + } else { + // This should never be false, it should throw an exception if it is, so we can just return the result + // But catch all else that miht go wrong + throw new playerException("creating the course."); + } + }// catch all else that might go wrong + catch (\Throwable $e){ + throw new playerException("creating the course" . $e); } + } + /** * Function to create a tenant. * @param $urltosend - URL retrieved from user in URL textbox. @@ -108,11 +131,9 @@ public function cmi5launch_create_tenant($newtenantname) { $settings = cmi5launch_settings($cmi5launchid); - //$actor = $USER->username; $username = $settings['cmi5launchbasicname']; $playerurl = $settings['cmi5launchplayerurl']; $password = $settings['cmi5launchbasepass']; - global $CFG; // Build URL for launch URL request. $url = $playerurl . "/api/v1/tenant"; @@ -130,19 +151,27 @@ public function cmi5launch_create_tenant($newtenantname) { // Sends the stream to the specified URL. $result = $this->cmi5launch_send_request_to_cmi5_player_post('cmi5launch_stream_and_send', $data, $url, $filetype, $username, $password); + // Check result and display message if not 200. $resulttest = $this->cmi5launch_connectors_error_message($result, "creating the tenant"); - - if ($resulttest == true) { - - // Decode returned response into array. - $returnedinfo = json_decode($result, true); - - // Return an array with tenant name and info. - return $returnedinfo; - } else { - return false; - }; + // why is it coming back null and shouldnt we go to else the? + + + // Now this will never return false, it will throw an exception if it fails, so we can just return the result + try { + if ($resulttest == true) { + + return $result; + } else { + + throw new playerException("creating the tenant."); + } + }// catch all else that might go wrong + catch (\Throwable $e){ + + throw new playerException("Uncaught error creating the tenant" . $e); + } + } /** @@ -169,13 +198,21 @@ public function cmi5launch_retrieve_registration_with_get($registration, $id) { // Check result and display message if not 200. $resulttest = $this->cmi5launch_connectors_error_message($result, "retrieving the registration"); - - if ($resulttest == true) { - - return $result; - } else { - return false; + + // Now this will never return false, it will throw an exception if it fails, so we can just return the result + try { + if ($resulttest == true) { + return $result; + } else { + + throw new playerException("retrieving the registration information."); + } + }// catch all else that might go wrong + catch (\Throwable $e){ + + throw new playerException("Uncaught error retrieving the registration information." . $e); } + } /** @@ -223,19 +260,26 @@ public function cmi5launch_retrieve_registration_with_post($courseid, $id) { // Check result and display message if not 200. $resulttest = $this->cmi5launch_connectors_error_message($result, "retrieving the registration"); - // Catch errors. - if ($resulttest == true) { - - $registrationinfo = json_decode($result, true); - - // The returned 'registration info' is a large json object. - // Code is the registration id we want. - $registration = $registrationinfo["code"]; - - return $registration; - } else { - return false; + // Now this will never return false, it will throw an exception if it fails, so we can just return the result + try { + if ($resulttest == true) { + + $registrationinfo = json_decode($result, true); + + // The returned 'registration info' is a large json object. + // Code is the registration id we want. + $registration = $registrationinfo["code"]; + + return $registration; + } else { + throw new playerException("retrieving the registration information."); + } + }// catch all else that might go wrong + catch (\Throwable $e){ + + throw new playerException("Uncaught error retrieving the registration information." . $e); } + } /** @@ -260,7 +304,6 @@ public function cmi5launch_retrieve_token($audience, $tenantid) { $username = $settings['cmi5launchbasicname']; $playerurl = $settings['cmi5launchplayerurl']; $password = $settings['cmi5launchbasepass']; - global $CFG; // Build URL for launch URL request. $url = $playerurl . "/api/v1/auth"; @@ -279,15 +322,23 @@ public function cmi5launch_retrieve_token($audience, $tenantid) { $result = $this->cmi5launch_send_request_to_cmi5_player_post('cmi5launch_stream_and_send', $data, $url, $filetype, $username, $password); // Check result and display message if not 200. - $resulttest = $this->cmi5launch_connectors_error_message($result, "retrieving the token"); + $resulttest = $this->cmi5launch_connectors_error_message($result, 'retrieving the tenant token.'); - if ($resulttest == true) { - $resultDecoded = json_decode($result, true); - $token = $resultDecoded['token']; + // Now this will never return false, it will throw an exception if it fails, so we can just return the result + try { + if ($resulttest == true) { - return $token; - } else { - return false; + $resultDecoded = json_decode($result, true); + $token = $resultDecoded['token']; + + return $token; + + } else { + throw new playerException("retrieving the tenant token."); + } + }// catch all else that might go wrong + catch (\Throwable $e){ + throw new playerException("Uncaught error retrieving the tenant token." . $e); } } @@ -340,18 +391,24 @@ public function cmi5launch_retrieve_url($id, $auindex) { $result = $this->cmi5launch_send_request_to_cmi5_player_post('cmi5launch_stream_and_send', $data, $url, $filetype, $token); // Check result and display message if not 200. - $resulttest = $this->cmi5launch_connectors_error_message($result, "retrieving launch url"); + $resulttest = $this->cmi5launch_connectors_error_message($result, "retrieving the launch url from player."); - // Catch errors. + // Now this will never return false, it will throw an exception if it fails, so we can just return the result + try { if ($resulttest == true) { - // Only return the URL. $urldecoded = json_decode($result, true); return $urldecoded; } else { - return false; + throw new playerException("retrieving the launch url from player."); } + }// catch all else that might go wrong + catch (\Throwable $e){ + throw new playerException("Uncaught error retrieving the launch url from player." . $e); + } + + } /** @@ -364,72 +421,96 @@ public function cmi5launch_retrieve_url($id, $auindex) { */ public function cmi5launch_send_request_to_cmi5_player_post($cmi5launch_stream_and_send, $databody, $url, $filetype, ...$tokenorpassword) { - // Assign passed in function to variable. - $stream = $cmi5launch_stream_and_send; - // Determine content type to be used in header. - // It is also the same as accepted type. - $contenttype = $filetype; - if ($contenttype == "zip") { - $contenttype = "application/zip\r\n"; - } else if ("json") { - $contenttype = "application/json\r\n"; - } - - // If number of args is greater than one it is for retrieving tenant info and args are username and password. - if (count($tokenorpassword) == 2 ) { + // Set error and exception handler to catch and override the default PHP error messages, to make messages more user friendly. + set_error_handler('mod_cmi5launch\local\sifting_data_warning', E_WARNING); + set_exception_handler('mod_cmi5launch\local\exception_au'); + + try { + + // I rhink this whole thing should be try catch cause there are several things that cango w + // Assign passed in function to variable. + $stream = $cmi5launch_stream_and_send; + // Determine content type to be used in header. + // It is also the same as accepted type. + $contenttype = $filetype; + if ($contenttype == "zip") { + $contenttype = "application/zip\r\n"; + } else if ("json") { + $contenttype = "application/json\r\n"; + } + + // If number of args is greater than one it is for retrieving tenant info and args are username and password. + if (count($tokenorpassword) == 2) { + + $username = $tokenorpassword[0]; + $password = $tokenorpassword[1]; + + + // Use key 'http' even if you send the request to https://... + // There can be multiple headers but as an array under the ONE header. + // Content(body) must be JSON encoded here, as that is what CMI5 player accepts. + $options = array( + 'http' => array( + 'method' => 'POST', + 'header' => array( + 'Authorization: Basic ' . base64_encode("$username:$password"), + "Content-Type: " . $contenttype . + "Accept: " . $contenttype + ), + 'content' => ($databody), + ), + ); + + //By calling the function this way, it enables encapsulation of the function and allows for testing. + //It is an extra step, but necessary for required PHP Unit testing. + $result = call_user_func($stream, $options, $url); + + + // Else the args are what we need for posting a course. + } else { + + // First arg will be token. + $token = $tokenorpassword[0]; + + // Use key 'http' even if you send the request to https://... + // There can be multiple headers but as an array under the ONE header + // content(body) must be JSON encoded here, as that is what CMI5 player accepts + // JSON_UNESCAPED_SLASHES used so http addresses are displayed correctly. + $options = array( + 'http' => array( + 'method' => 'POST', + 'ignore_errors' => true, + 'header' => array( + "Authorization: Bearer " . $token, + "Content-Type: " . $contenttype . + "Accept: " . $contenttype + ), + 'content' => ($databody), + ), + ); + + + //By calling the function this way, it enables encapsulation of the function and allows for testing. + //It is an extra step, but necessary for required PHP Unit testing. + $result = call_user_func($stream, $options, $url); + + // Ok, calling it throuw the third party isn't workin, what if we mock call_user_func instead and have an eror thrown there + } + + + // Restore default hadlers. + restore_exception_handler(); + restore_error_handler(); + + // Return response. + return $result; - $username = $tokenorpassword[0]; - $password = $tokenorpassword[1]; + }catch(\Throwable $e) { + // + throw new playerException("communicating with player, sending or crafting a POST request: " . $e); + } - // Use key 'http' even if you send the request to https://... - // There can be multiple headers but as an array under the ONE header. - // Content(body) must be JSON encoded here, as that is what CMI5 player accepts. - $options = array( - 'http' => array( - 'method' => 'POST', - 'header' => array('Authorization: Basic '. base64_encode("$username:$password"), - "Content-Type: " .$contenttype . - "Accept: " . $contenttype), - 'content' => ($databody), - ), - ); - - //By calling the function this way, it enables encapsulation of the function and allows for testing. - //It is an extra step, but necessary for required PHP Unit testing. - $result = call_user_func($stream, $options, $url); - - - // Else the args are what we need for posting a course. - } else { - - // First arg will be token. - $token = $tokenorpassword[0]; - - // Use key 'http' even if you send the request to https://... - // There can be multiple headers but as an array under the ONE header - // content(body) must be JSON encoded here, as that is what CMI5 player accepts - // JSON_UNESCAPED_SLASHES used so http addresses are displayed correctly. - $options = array( - 'http' => array( - 'method' => 'POST', - 'ignore_errors' => true, - 'header' => array("Authorization: Bearer ". $token, - "Content-Type: " .$contenttype . - "Accept: " . $contenttype), - 'content' => ($databody), - ), - ); - - - //By calling the function this way, it enables encapsulation of the function and allows for testing. - //It is an extra step, but necessary for required PHP Unit testing. - $result = call_user_func($stream, $options, $url); - - } - - // Return response. - return $result; } /** @@ -455,21 +536,18 @@ public function cmi5launch_send_request_to_cmi5_player_get($cmi5launch_stream_an ), ); - //$helper = new cmi5launch_helpers; - // $stream = cmi5launch_stream_and_send(); - // Sends the stream to the specified URL and stores results. - // The false is use_include_path, which we dont want in this case, we want to go to the url. - //$launchresponse = cmi5launch_stream_and_send( $options, $url ); - + try { //By calling the function this way, it enables encapsulation of the function and allows for testing. //It is an extra step, but necessary for required PHP Unit testing. $result = call_user_func($stream, $options, $url); - - - $sessiondecoded = json_decode($result, true); - // Return response. - return $sessiondecoded; + // Return response. + return $result; + + } catch (\Throwable $e) { + // echo" are we here?"; + throw new playerException("communicating with player, sending or crafting a GET request: " . $e); + } } /** @@ -494,13 +572,21 @@ public function cmi5launch_retrieve_session_info_from_player($sessionid, $id) { $result = $this->cmi5launch_send_request_to_cmi5_player_get('cmi5launch_stream_and_send', $token, $url); // Check result and display message if not 200. - $resulttest = $this->cmi5launch_connectors_error_message($result, "retrieving session info"); + $resulttest = $this->cmi5launch_connectors_error_message($result, "retrieving the session information."); - if ($resulttest == true) { + // Now this will never return false, it will throw an exception if it fails, so we can just return the result + try { + if ($resulttest == true) { - return $result; - } else { - return false; + return $result; + + } else { + throw new playerException("retrieving the session information."); + } + }// catch all else that might go wrong + catch (\Throwable $e){ + + throw new playerException("Uncaught error retrieving the session information." . $e); } } @@ -513,8 +599,9 @@ public function cmi5launch_retrieve_session_info_from_player($sessionid, $id) { * @param string $type - The type missing to be added to the error message. * @return bool */ - public static function cmi5launch_connectors_error_message($resulttotest, $type) { - + public function cmi5launch_connectors_error_message($resulttotest, $type) { + + // Decode result because if it is not 200 then something went wrong // If it's a string, decode it. if (is_string($resulttotest)) { @@ -527,26 +614,24 @@ public static function cmi5launch_connectors_error_message($resulttotest, $type) // Player cannot return an error if not runnin, if ($resulttest === false ){ - echo "
"; - - echo "Something went wrong " . $type . ". CMI5 Player is not communicating. Is it running?"; - - echo "
"; + + $errormessage = $type . ". CMI5 Player is not communicating. Is it running?"; - return false; + throw new playerException($errormessage); } else if( array_key_exists("statusCode", $resulttest) && $resulttest["statusCode"] != 200) { - echo "
"; + + $errormessage = $type . " CMI5 Player returned " . $resulttest["statusCode"] . " error. With message '" + . $resulttest["message"] . "'." ; - echo "Something went wrong " . $type . ". CMI5 Player returned " . $resulttest["statusCode"] . " error. With message '" - . $resulttest["message"] . "'." ; - echo "
"; - - return false; + // echo"whatt is error messae before throwing::: " . $errormessage; + // echo" what is error messae: " . $errormessage;""; + throw new playerException($errormessage); + } else { - - // No errors, continue. + // No errors, continue. + return true; } } diff --git a/classes/local/errorover.php b/classes/local/errorover.php index a026080..5e60a27 100644 --- a/classes/local/errorover.php +++ b/classes/local/errorover.php @@ -159,4 +159,41 @@ public function customFunction() { echo " This error to string :"; // $this->getTraceAsString(); } -} \ No newline at end of file +} +/** + * Define a custom exception class, this will make pour tests meaningful + * from php webpage: "Custom exception classes can allow you to write tests that prove your exceptions + * are meaningful. Usually testing exceptions, you either assert the message equals +*something in which case you can't change the message format without refactoring, +*or not make any assertions at all in which case you can get misleading messages +*later down the line. Especially if your $e->getMessage is something complicated +*like a var_dump'ed context array." + */ +class playerException extends \Exception +{ + // Redefine the exception so message isn't optional + // I want an exception that takkkes what is missing and adds it to messsssage? + // Is this possivlbe? + public function __construct($message, $code = 0, Throwable $previous = null) { + // some code + + // Ah maybe here is where I can differentiate them + $playermessage = "Player communication error. Something went wrong " . $message; + // make sure everything is assigned properly + parent::__construct($playermessage, $code, $previous); + } + + + // custom string representation of object (what is returned with echo) + public function __toString(): string { + return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; + // maybe here? + } + + public function customFunction() { + echo " This error to string :"; + // $this->getTraceAsString(); + } +} + +// If all my exceptions are the same, just diff names, are they necessary? Do any of my try/catches really differentiate? \ No newline at end of file diff --git a/cmi5PHP/tests/auTest.php b/cmi5PHP/tests/auTest.php index 96efa58..7d36302 100644 --- a/cmi5PHP/tests/auTest.php +++ b/cmi5PHP/tests/auTest.php @@ -164,7 +164,7 @@ public function testInstantiation_except_null() // Expected message // Catch the exception. $this->expectException(nullException::class); - $this->expectExceptionMessage("Statement to build AU is null or not an array." ); + $this->expectExceptionMessage("Statement to build AU is null or not an array/object." ); $obj = new au($nullstatement); @@ -181,9 +181,11 @@ public function testInstantiation_except_nonarray() // Catch the exception. $this->expectException(nullException::class); - $this->expectExceptionMessage("Statement to build AU is null or not an array." ); + $this->expectExceptionMessage("Statement to build AU is null or not an array/object." ); $obj = new au($nullstatement); } + + } \ No newline at end of file diff --git a/cmi5PHP/tests/ausHelpersTest.php b/cmi5PHP/tests/ausHelpersTest.php index 9ed7c3e..3d4584e 100644 --- a/cmi5PHP/tests/ausHelpersTest.php +++ b/cmi5PHP/tests/ausHelpersTest.php @@ -347,42 +347,7 @@ public function testcmi5launch_create_aus_exception() $helper->cmi5launch_create_aus($teststatements); } - // Test saving aus, this function returns ids, so we can make a stub which just returns ids. - // This will test it is called without messing with the DB. - public function testcmi5launch_save_aus() - { - // Make a global variable to hold the id's to pretend to be cmi5launch instance id. - global $cmi5launch, $auidForTest; - - $cmi5launch = new \stdClass(); - $cmi5launch->id = 1; - - // The func should return auids created by the DB when AU's were saved in array format. - $helper = new au_helpers(); - - //Lets create 4 aus statement - for ($i = 0; $i < 3; $i++) { - $testAus[$i][] = $this->mockStatement2; - } - - //So now with this fake 'statement', lets ensure it pulls the correct value which is "correct Retrieval" - $returnedAUids = $helper->cmi5launch_save_aus($helper->cmi5launch_create_aus($testAus)); - - // First make sure array is returned - $this->assertIsArray($returnedAUids, "Expected retrieved statement to be an array"); - - // The array should have the same count of ids as AU's passed in - $this->assertCount(3, $returnedAUids, "Expected retrieved statement to have three aus"); - - // Now iterate through the returned array and ensure ids were passed back, numeric ids - foreach ($returnedAUids as $auId) { - - $this->assertIsNumeric($auId, "Expected array to have numeric values"); - } - - - $auidForTest = $returnedAUids; - } + // test saving aus with exceptions. public function testcmi5launch_save_aus_exceptions() @@ -468,18 +433,67 @@ public function testcmi5launch_save_aus_exceptions_test_null() } + // Test saving aus, this function returns ids, so we can make a stub which just returns ids. + // This will test it is called without messing with the DB. + public function testcmi5launch_save_aus() + { + // Make a global variable to hold the id's to pretend to be cmi5launch instance id. + global $cmi5launch, $auidForTest; + + $cmi5launch = new \stdClass(); + $cmi5launch->id = 1; + + // The func should return auids created by the DB when AU's were saved in array format. + $helper = new au_helpers(); + + //Lets create 4 aus statement + for ($i = 0; $i < 3; $i++) { + $testAus[$i][] = $this->mockStatement2; + } + + //So now with this fake 'statement', lets ensure it pulls the correct value which is "correct Retrieval" + $returnedAUids = $helper->cmi5launch_save_aus($helper->cmi5launch_create_aus($testAus)); + // First make sure array is returned + $this->assertIsArray($returnedAUids, "Expected retrieved statement to be an array"); + + // The array should have the same count of ids as AU's passed in + $this->assertCount(3, $returnedAUids, "Expected retrieved statement to have three aus"); + + // Now iterate through the returned array and ensure ids were passed back, numeric ids + foreach ($returnedAUids as $auId) { + + $this->assertIsNumeric($auId, "Expected array to have numeric values"); + } + + // Is this not savin? + // echo "Returned AU ids: " . var_dump($returnedAUids) . "\n"; + + $auidForTest = $returnedAUids; + } // Test retrieving an AU from the DB with a correct value. public function testcmi5launch_retrieve_aus_from_db() { - // Access the global array of ids from above test + // ok, what if we saved ere the retrieved + // Access the global array of ids from above test global $auidForTest; // global $auidForTest; $helper = new au_helpers(); + // Save new aus to db to pull. + //Lets create 4 aus statement + for ($i = 0; $i < 3; $i++) { + $testAus[$i][] = $this->mockStatement2; + } + + //So now with this fake 'statement', lets ensure it pulls the correct value which is "correct Retrieval" + $returnedAUids = $helper->cmi5launch_save_aus($helper->cmi5launch_create_aus($testAus)); + + //and what is here? + // echo"au id for test: " . var_dump($auidForTest) . "\n"; // It takes singular ids, so we will iterate through them - foreach ($auidForTest as $auId) { + foreach ($returnedAUids as $auId) { $returnedAu = $helper->cmi5launch_retrieve_aus_from_db($auId); diff --git a/cmi5PHP/tests/cmi5TestHelpers.php b/cmi5PHP/tests/cmi5TestHelpers.php index db477f5..5db6485 100644 --- a/cmi5PHP/tests/cmi5TestHelpers.php +++ b/cmi5PHP/tests/cmi5TestHelpers.php @@ -532,5 +532,11 @@ function cmi5launch_test_stream_and_send_fail($options, $url) // Lets pass in the 'return' value as the option. return $errormessage; } + // should I have it throw an error? would that work, or would that take the erorr out of SUT? + // So now all the test has to do is inject THIS which will return as we please + function cmi5launch_test_stream_and_send_excep($options, $url) + { + throw new \Exception('test error'); + } ?> diff --git a/cmi5PHP/tests/cmi5_connectorsTest.php b/cmi5PHP/tests/cmi5_connectorsTest.php index e0d28d3..0028976 100755 --- a/cmi5PHP/tests/cmi5_connectorsTest.php +++ b/cmi5PHP/tests/cmi5_connectorsTest.php @@ -5,6 +5,8 @@ use PHPUnit\Framework\TestCase; use mod_cmi5launch\local\cmi5_connectors; use mod_cmi5launch\test\cmi5TestHelpers; +use mod_cmi5launch\local\playerException; + require_once( "cmi5TestHelpers.php"); /** @@ -33,7 +35,7 @@ public static function setUpBeforeClass(): void { global $DB, $cmi5launch, $cmi5launchid; - // Mke a fake cmi5 launch record. + // Make a fake cmi5 launch record. $cmi5launchid = maketestcmi5launch(); } @@ -51,7 +53,8 @@ protected function setUp(): void { global $DB, $cmi5launch, $cmi5launchid, $USER, $testcourseid, $cmi5launchsettings; - $cmi5launchsettings = array("cmi5launchtenanttoken" => "Testtoken", "cmi5launchplayerurl" => "http://test/launch.php", "cmi5launchcustomacchp" => "http://testhomepage.com"); + $cmi5launchsettings = array("cmi5launchtenanttoken" => "Testtoken", "cmi5launchplayerurl" => "http://test/launch.php", "cmi5launchcustomacchp" => "http://testhomepage.com", + "cmi5launchbasicname" => 'testname', "cmi5launchbasepass" => "testpassword"); // Override global variable and function so that it returns test data. @@ -129,9 +132,8 @@ public function testcmi5launch_create_course_fail_with_message() // Function that will be called in function under test. $testfunction = 'cmi5launch_stream_and_send'; - // Message we expect to be output. - $expectedstring= "
Something went wrong creating the course. CMI5 Player returned 404 error. With message 'testmessage'.
"; - // Arguments to be passed to the method under test. + + // Arguments to be passed to the method under test. $id = 0; $tenanttoken = "testtoken"; // Error message for stubbed method to return. @@ -143,7 +145,10 @@ public function get_content() { return "testfilecontents"; } }; - + $test = false; + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong creating the course. CMI5 Player returned 404 error. With message 'testmessage'." ; + // Mock a cmi5 connector object but only stub ONE method, as we want to test the other methods. // Create a mock of the send_request class as we don't actually want // to create a new course in the player. @@ -158,16 +163,77 @@ public function get_content() { ->with($testfunction, 'testfilecontents', 'http://test/launch.php/api/v1/course', 'zip', 'testtoken') ->willReturn($errormessage); - // Call the method under test. - $returnedresult =$csc->cmi5launch_create_course($id, $tenanttoken, $filename); + // - // Result should be debug echo string and false. - $this->assertNotTrue($returnedresult, "Expected retrieved object to be false"); - //And it should output this error message - $this->expectOutputString($expectedstring); + // Wait, i bet this is being thrown in the cmi5 connectors error message func and so we need to catch + // the correct output not an exception + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + // Call the method under test. + $returnedresult = $csc->cmi5launch_create_course($id, $tenanttoken, $filename); + } + /** + * Test of the cmi5launch_create_course method with a failed response from the player. + * This one tests if resulttest is false. This path shouldnt be able to be reached but is here to test the failsafe. + * @return void + */ + public function testcmi5launch_create_course_fail_with_exception() + { + + // Function that will be called in function under test. + $testfunction = 'cmi5launch_stream_and_send'; + // Arguments to be passed to the method under test. + $id = 0; + $tenanttoken = "testtoken"; + // Error message for stubbed method to return. + $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); + + //If we make filename an object with it's own get_content method, we can stub it out. + $filename = new class { + public function get_content() { + return "testfilecontents"; + } + }; + $test = false; + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong creating the course." ; + + // Mock a cmi5 connector object but only stub ONE method, as we want to test the other methods. + // Create a mock of the send_request class as we don't actually want + // to create a new course in the player. + $csc = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_post', 'cmi5launch_connectors_error_message')) + ->getMock(); + + // We will have the mock return a fake message as if the player had a problem with request. + // This should enable us to test the method under failing conditions. We do expect create_course to only call this once. + $csc->expects($this->once()) + ->method('cmi5launch_send_request_to_cmi5_player_post') + ->with($testfunction, 'testfilecontents', 'http://test/launch.php/api/v1/course', 'zip', 'testtoken') + ->willReturn($errormessage); + + // The string just needs to be returned as is. We do expect create_tenant to only call this once. + $csc->expects($this->once()) + ->method('cmi5launch_connectors_error_message') + ->with($errormessage, 'creating the course') // for tomorrow, is thi failing because with only evaluates strings? Like do we need to string the array out> + // IRL it returns something that needs to be json decoded, so lets pass somethin that is encoded> + ->willReturn(false); + + // + + // Wait, i bet this is being thrown in the cmi5 connectors error message func and so we need to catch + // the correct output not an exception + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + // Call the method under test. + $returnedresult = $csc->cmi5launch_create_course($id, $tenanttoken, $filename); + + } /** * Test of the cmi5launch_create_tenant method with a successful response from the player. * @return void @@ -179,21 +245,14 @@ public function testcmi5launch_create_tenant_pass() $testfunction = 'cmi5launch_stream_and_send'; // Arguments to be passed to the method under test. - $urltosend = "playerwebaddress"; - $username = "testname"; - $password = "testpassword"; - $newtenantname = "testtenantname"; - $data = array ('code' => 'testtenantname'); + $newtenantname = "newtenantname"; + $data = array ('code' => 'newtenantname'); // Encode data as it will be encoded when sent to player $data = json_encode($data); - - // This is the expected return value. - $returnvalue = array( - "code" => "testtenantname", - "id" => 9 - ); - - + + // Message for stubbed method to return. + $returnvalue = json_encode(array("statusCode" => "200", "code" => "newtenantname","id" => "9" ) ); + // Mock a cmi5 connector object but only stub ONE method, as we want to test the other methods. // Create a mock of the send_request class as we don't actually want // to create a new tenant. @@ -205,24 +264,24 @@ public function testcmi5launch_create_tenant_pass() // The string just needs to be returned as is. We do expect create_tenant to only call this once. $csc->expects($this->once()) ->method('cmi5launch_send_request_to_cmi5_player_post') - ->with($testfunction, $data, 'playerwebaddress','json', 'testname', 'testpassword') // for tomorrow, is thi failing because with only evaluates strings? Like do we need to string the array out> + ->with($testfunction, $data, 'http://test/launch.php/api/v1/tenant','json', 'testname', 'testpassword') // for tomorrow, is thi failing because with only evaluates strings? Like do we need to string the array out> // IRL it returns something that needs to be json decoded, so lets pass somethin that is encoded> - ->willReturn('{ - "code": "testtenantname", - "id": 9 - }' - ); + ->willReturn($returnvalue); + //Call the method under test. - $result =$csc->cmi5launch_create_tenant($urltosend, $username, $password, $newtenantname); + $result =$csc->cmi5launch_create_tenant( $newtenantname); + // And the return should be a string (the original method returns what the player sends back json-decoded or FALSE) - $this->assertIsArray($result); + $this->assertIsString($result); $this->assertEquals( $returnvalue, $result); + } + /** - * Test of the cmi5launch_create_tenant method with a error response from the player. + * Test of the cmi5launch_create_tenant method with a failed response from the player. Should trigger an exception. * @return void */ public function testcmi5launch_create_tenant_fail() @@ -232,44 +291,103 @@ public function testcmi5launch_create_tenant_fail() $testfunction = 'cmi5launch_stream_and_send'; // Arguments to be passed to the method under test. - $urltosend = "playerwebaddress"; - $username = "testname"; - $password = "testpassword"; - $newtenantname = "testtenantname"; - $data = array ('code' => 'testtenantname'); + + $newtenantname = "newtenantname"; + $data = array ('code' => 'newtenantname'); // Encode data as it will be encoded when sent to player $data = json_encode($data); - // The expected error message to be output. - $expectedstring= "
Something went wrong creating the tenant. CMI5 Player returned 404 error. With message 'testmessage'.
"; + + // Message for stubbed method to return. + $errormessage = array("statusCode" => "400", "message" => "website not found","id" => "9" ); + - // Error message for stubbed method to return. - $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); - - // Mock a cmi5 connector object but only stub ONE method, as we want to test the others. + // Mock a cmi5 connector object but only stub ONE method, as we want to test the other methods. // Create a mock of the send_request class as we don't actually want - // to create a new course in the player. + // to create a new tenant. $csc = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_post')) ->getMock(); - - // We will have the mock return a fake message as if the player had a problem with request. - // This will enable us to test the method under failing conditions. We do expect create_tenant to only call this once. + // We will have the mock return a basic string, as it's not under test. + // The string just needs to be returned as is. We do expect create_tenant to only call this once. $csc->expects($this->once()) ->method('cmi5launch_send_request_to_cmi5_player_post') - ->with($testfunction, $data, 'playerwebaddress', 'json', 'testname', 'testpassword') + ->with($testfunction, $data, 'http://test/launch.php/api/v1/tenant','json', 'testname', 'testpassword') // for tomorrow, is thi failing because with only evaluates strings? Like do we need to string the array out> + // IRL it returns something that needs to be json decoded, so lets pass somethin that is encoded> ->willReturn($errormessage); - //Call the method under test. - $result =$csc->cmi5launch_create_tenant($urltosend, $username, $password, $newtenantname); + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the registration information. CMI5 Player returned 400 error. With message 'website not found'." ; + - // Result should be debug echo string and false - $this->assertNotTrue($result, "Expected retrieved object to be false"); - // And it should output this error message - $this->expectOutputString($expectedstring); + // the correct output not an exception + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result =$csc->cmi5launch_create_tenant( $newtenantname); + } + + /** + * Test of the cmi5launch_create_tenant method with a failed response from the player. Should trigger an exception. + * This one tests if resulttest is false. This path shouldnt be able to be reached but is here to test the failsafe. + * @return void + */ + public function testcmi5launch_create_tenant_fail_2() + { + + // Function that will be called in function under test. + $testfunction = 'cmi5launch_stream_and_send'; + + // Arguments to be passed to the method under test. + + $newtenantname = "newtenantname"; + $data = array ('code' => 'newtenantname'); + // Encode data as it will be encoded when sent to player + $data = json_encode($data); + + + // Message for stubbed method to return. + $errormessage = array("statusCode" => "400", "message" => "website not found","id" => "9" ); + + + // Mock a cmi5 connector object but only stub ONE method, as we want to test the other methods. + // Create a mock of the send_request class as we don't actually want + // to create a new tenant. + $csc = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_post','cmi5launch_connectors_error_message')) + ->getMock(); + + // We will have the mock return a basic string, as it's not under test. + // The string just needs to be returned as is. We do expect create_tenant to only call this once. + $csc->expects($this->once()) + ->method('cmi5launch_send_request_to_cmi5_player_post') + ->with($testfunction, $data, 'http://test/launch.php/api/v1/tenant','json', 'testname', 'testpassword') // for tomorrow, is thi failing because with only evaluates strings? Like do we need to string the array out> + // IRL it returns something that needs to be json decoded, so lets pass somethin that is encoded> + ->willReturn($errormessage); + // We will have the mock return a basic string, as it's not under test. + // The string just needs to be returned as is. We do expect create_tenant to only call this once. + $csc->expects($this->once()) + ->method('cmi5launch_connectors_error_message') + ->with($errormessage, "creating the tenant") // for tomorrow, is thi failing because with only evaluates strings? Like do we need to string the array out> + // IRL it returns something that needs to be json decoded, so lets pass somethin that is encoded> + ->willReturn(false); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong creating the tenant." ; + + + // the correct output not an exception + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result =$csc->cmi5launch_create_tenant( $newtenantname); + + } /** * * Test the retrieve_registration method with a successful response from the player. * @return void @@ -327,7 +445,7 @@ public function testcmi5launch_retrieve_registration_with_get_fail() $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); // The expected error message to be output. - $expectedstring= "
Something went wrong retrieving the registration. CMI5 Player returned 404 error. With message 'testmessage'.
"; + // $expectedstring= "
Something went wrong retrieving the registration. CMI5 Player returned 404 error. With message 'testmessage'.
"; // The arguments to be passed to the method under test. $id = 0; @@ -350,13 +468,68 @@ public function testcmi5launch_retrieve_registration_with_get_fail() ->with($testfunction, "Testtoken", $urltosend) ->willReturn($errormessage); + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the registration. CMI5 Player returned 404 error. With message 'testmessage'" ; + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result = $mockedclass->cmi5launch_retrieve_registration_with_get($registration, $id); + + } + + /** + * * Test the retrieve_registration method with a failed response from the player. + * This one tests if resulttest is false. This path shouldnt be able to be reached but is here to test the failsafe. + * @return void + */ + public function testcmi5launch_retrieve_registration_with_get_fail_2() + { + // Error message for stubbed method to return. + $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); + + // The expected error message to be output. + // $expectedstring= "
Something went wrong retrieving the registration. CMI5 Player returned 404 error. With message 'testmessage'.
"; + + // The arguments to be passed to the method under test. + $id = 0; + $registration = "testregistration"; + + // This is the url the stubbed method shopuld receive. + $urltosend = "http://test/launch.php/api/v1/registration/testregistration"; + + // Function that will be called in function under test. + $testfunction = 'cmi5launch_stream_and_send'; + + + // Mock a cmi5 connector object but only stub ONE method, as we want to test the others. + $mockedclass = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_get', 'cmi5launch_connectors_error_message')) + ->getMock(); + + $mockedclass->expects($this->once()) + ->method('cmi5launch_send_request_to_cmi5_player_get') + ->with($testfunction, "Testtoken", $urltosend) + ->willReturn($errormessage); + + + $mockedclass->expects($this->once()) + ->method('cmi5launch_connectors_error_message') + ->with($errormessage, "retrieving the registration") + ->willReturn(false); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the registration information." ; + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + //Call the method under test. $result = $mockedclass->cmi5launch_retrieve_registration_with_get($registration, $id); - // Result should be debug echo string and false - $this->assertNotTrue($result, "Expected retrieved object to be false"); - //And it should output this error message - $this->expectOutputString($expectedstring); } /** @@ -416,17 +589,72 @@ public function testcmi5launch_retrieve_registration_with_post_pass() } /** - * * Test the retrieve_registration_with_post method with a failed response from the player. + * * Test the retrieve_registration_with_post method with a failed response from the player. Should throw exception. + * This one tests if resulttest is false. This path shouldnt be able to be reached but is here to test the failsafe. * @return void */ - public function testcmi5launch_retrieve_registration_with_post_fail() + public function testcmi5launch_retrieve_registration_with_post_fail_2() { // Error message for stubbed method to return. $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); + // The arguments to be passed to the method under test. + $id = 0; + $courseid = 1; + $filetype = "json"; - // The expected error message to be output. - $expectedstring= "
Something went wrong retrieving the registration. CMI5 Player returned 404 error. With message 'testmessage'.
"; + // The data to be passed to the mocked method. + $data = array( + "courseId" => $courseid, + "actor" => array( + "account" => array ( + "homePage" => "http://testhomepage.com", + "name" => "testname" + ), + ), + ); + + // This is the url the stubbed method shopuld receive. + $urltosend = "http://test/launch.php/api/v1/registration"; + + // Function that will be called in function under test. + $testfunction = 'cmi5launch_stream_and_send'; + + // Mock a cmi5 connector object but only stub ONE method, as we want to test the others. + $mockedclass = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_post', 'cmi5launch_connectors_error_message')) + ->getMock(); + + // Mock returns json encoded data, as it would be from the player. + $mockedclass->expects($this->once()) + ->method('cmi5launch_send_request_to_cmi5_player_post') + ->with($testfunction, json_encode($data), $urltosend, $filetype, "Testtoken") + ->willReturn($errormessage); + + + $mockedclass->expects($this->once()) + ->method('cmi5launch_connectors_error_message') + ->with($errormessage, "retrieving the registration") + ->willReturn(false); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the registration information." ; + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result = $mockedclass->cmi5launch_retrieve_registration_with_post($courseid, $id); + } + /** + * * Test the retrieve_registration_with_post method with a failed response from the player. Should throw exception. + * @return void + */ + public function testcmi5launch_retrieve_registration_with_post_fail() + { + // Error message for stubbed method to return. + $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); // The arguments to be passed to the method under test. $id = 0; $courseid = 1; @@ -460,15 +688,17 @@ public function testcmi5launch_retrieve_registration_with_post_fail() ->with($testfunction, json_encode($data), $urltosend, $filetype, "Testtoken") ->willReturn($errormessage); - //Call the method under test. - $result = $mockedclass->cmi5launch_retrieve_registration_with_post($courseid, $id); + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the registration. CMI5 Player returned 404 error. With message 'testmessage'" ; - // Result should be debug echo string and false - $this->assertNotTrue($result, "Expected retrieved object to be false"); - //And it should output this error message - $this->expectOutputString($expectedstring); - } + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result = $mockedclass->cmi5launch_retrieve_registration_with_post($courseid, $id); + } /** * * Test the retrieve_token method with a successful response from the player. @@ -476,14 +706,19 @@ public function testcmi5launch_retrieve_registration_with_post_fail() */ public function testcmi5launch_retrieve_token_pass() { + global $CFG, $cmi5launchid; + + $settings = cmi5launch_settings($cmi5launchid); // Arguments to be passed to the method under test. - $url = "http://test/launch.php"; - $username = "testname"; + //$url = "http://test/launch.php"; + // $username = "testname"; $filetype = "json"; - $password = "testpassword"; + //$password = "testpassword"; $audience = "testaudience"; $tenantid = 0; - + $username = $settings['cmi5launchbasicname']; + $url = $settings['cmi5launchplayerurl'] . "/api/v1/auth"; + $password = $settings['cmi5launchbasepass']; // The data to be passed to the mocked method. $data = array( "tenantId" => $tenantid, @@ -491,9 +726,10 @@ public function testcmi5launch_retrieve_token_pass() ); // The player returns a json string. - $returnvalue = '{ - "token": "testtoken" - }'; + $returnvalue = "testtoken"; + + $playervalue = array("statusCode" => "200", "token" => "testtoken","message" => "testmessage" ); + // Function that will be called in function under test. $testfunction = 'cmi5launch_stream_and_send'; @@ -505,10 +741,10 @@ public function testcmi5launch_retrieve_token_pass() $mockedclass->expects($this->once()) ->method('cmi5launch_send_request_to_cmi5_player_post') ->with($testfunction, json_encode($data), $url, $filetype, $username, $password) - ->willReturn($returnvalue); + ->willReturn(json_encode($playervalue)); //Call the method under test. - $result = $mockedclass->cmi5launch_retrieve_token($url, $username, $password, $audience, $tenantid); + $result = $mockedclass->cmi5launch_retrieve_token($audience, $tenantid); // And the return should be a string (the original method returns what the player sends back or FALSE. $this->assertIsString($result); @@ -521,18 +757,21 @@ public function testcmi5launch_retrieve_token_pass() */ public function testcmi5launch_retrieve_token_fail() { + global $CFG, $cmi5launchid; + $settings = cmi5launch_settings($cmi5launchid); + // Error message for stubbed method to return. $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); - // The expected error message to be output. - $expectedstring= "
Something went wrong retrieving the token. CMI5 Player returned 404 error. With message 'testmessage'.
"; - // Arguments to be passed to the method under test. - $url = "http://test/launch.php"; - $username = "testname"; - $password = "testpassword"; + $url = $settings['cmi5launchplayerurl'] . '/api/v1/auth'; + $audience = "testaudience"; $tenantid = 0; + + //$actor = $USER->username; + $username = $settings['cmi5launchbasicname']; + $password = $settings['cmi5launchbasepass']; // The data to be passed to the mocked method. $filetype = "json"; @@ -555,13 +794,80 @@ public function testcmi5launch_retrieve_token_fail() ->with($testfunction, json_encode($data), $url, $filetype, $username, $password) ->willReturn($errormessage); + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the tenant token. CMI5 Player returned 404 error. With message 'testmessage'" ; + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + + //Call the method under test. + $result = $mockedclass->cmi5launch_retrieve_token($audience, $tenantid); + + } + + /** + * * Test the retrieve_token method with a failed response from the player. + * This one tests if resulttest is false. This path shouldnt be able to be reached but is here to test the failsafe. + * @return void + */ + public function testcmi5launch_retrieve_token_fail_2() + { + global $CFG, $cmi5launchid; + $settings = cmi5launch_settings($cmi5launchid); + + // Error message for stubbed method to return. + $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); + + // Arguments to be passed to the method under test. + $url = $settings['cmi5launchplayerurl'] . '/api/v1/auth'; + + $audience = "testaudience"; + $tenantid = 0; + + //$actor = $USER->username; + $username = $settings['cmi5launchbasicname']; + $password = $settings['cmi5launchbasepass']; + + // The data to be passed to the mocked method. + $filetype = "json"; + $data = array( + "tenantId" => $tenantid, + "audience" => $audience, + ); + // Function that will be called in function under test. + $testfunction = 'cmi5launch_stream_and_send'; + + + // Mock a cmi5 connector object but only stub ONE method, as we want to test the others. + $mockedclass = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors' ) + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_post', 'cmi5launch_connectors_error_message')) + ->getMock(); + + // Mock returns json encoded data, as it would be from the player. + $mockedclass->expects($this->once()) + ->method('cmi5launch_send_request_to_cmi5_player_post') + ->with($testfunction, json_encode($data), $url, $filetype, $username, $password) + ->willReturn($errormessage); + + // Mock returns json encoded data, as it would be from the player. + $mockedclass->expects($this->once()) + ->method('cmi5launch_connectors_error_message') + ->with($errormessage, 'retrieving the tenant token.') + ->willReturn(false); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the tenant token." ; + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. - $result = $mockedclass->cmi5launch_retrieve_token($url, $username, $password, $audience, $tenantid); + $result = $mockedclass->cmi5launch_retrieve_token($audience, $tenantid); - // Result should be debug echo string and false. - $this->assertNotTrue($result, "Expected retrieved object to be false"); - // And it should output this error message. - $this->expectOutputString($expectedstring); } /** @@ -683,13 +989,93 @@ public function testcmi5launch_retrieve_url_fail() ->with($testfunction, json_encode($data), $urltosend, $filetype, "Testtoken") ->willReturn($errormessage); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the launch url from player. CMI5 Player returned 404 error. With message 'testmessage'" ; + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + //Call the method under test. $result = $mockedclass->cmi5launch_retrieve_url($id, $auindex); - // Result should be debug echo string and false - $this->assertNotTrue($result, "Expected retrieved object to be false"); - //And it should output this error message - $this->expectOutputString($expectedstring); + + } + + /** + * * Test the retrieve_url (launchurl) method with a failed response from the player. + * This one tests if resulttest is false. This path shouldnt be able to be reached but is here to test the failsafe. + * @return void + */ + public function testcmi5launch_retrieve_url_fail_2() + { + global $cmi5launchid; + + // Error message for stubbed method to return. + $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); + + // The id to be passed to method under test. + $id = $cmi5launchid; + $auindex = 1; + $filetype = "json"; + + //Retrieve settings like the method under test will. + $settings = cmi5launch_settings($id); + $playerurl = $settings['cmi5launchplayerurl']; + + $returnurl = "https://testmoodle.com"; + $registrationid = "testregistrationid"; + + // The data to be passed to the mocked method. + $data = array( + 'actor' => array( + 'account' => array( + "homePage" => "http://testhomepage.com", + "name" => "testname", + ), + ), + 'returnUrl' => $returnurl, + 'reg' => $registrationid, + ); + + // This is the url the stubbed method shopuld receive. + $urltosend = $playerurl . "/api/v1/course/" . "1" ."/launch-url/" . $auindex; + + // Function that will be called in function under test. + $testfunction = 'cmi5launch_stream_and_send'; + + // Mock a cmi5 connector object but only stub ONE method, as we want to test the other. + // Create a mock of the send_request class as we don't actually want + // to create a new course in the player. + $mockedclass = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_post', 'cmi5launch_connectors_error_message')) + ->getMock(); + + // Mock returns json encoded data, as it would be from the player. + $mockedclass->expects($this->once()) + ->method('cmi5launch_send_request_to_cmi5_player_post') + ->with($testfunction, json_encode($data), $urltosend, $filetype, "Testtoken") + ->willReturn($errormessage); + + + // Mock returns json encoded data, as it would be from the player. + $mockedclass->expects($this->once()) + ->method('cmi5launch_connectors_error_message') + ->with($errormessage, 'retrieving the launch url from player.') + ->willReturn(false); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the launch url from player." ; + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result = $mockedclass->cmi5launch_retrieve_url($id, $auindex); + + } /** @@ -728,17 +1114,80 @@ public function testcmi5launch_send_request_to_cmi5_player_post_with_one_arg() //The arguments to pass in, in this case one, a pretend token. $token = "testtoken"; + // Expected exceptions + $exceptionmessage = "Player communication error. Error communicating with player, sending a POST request." ; + + // Expected exceptions and messages + // $this->expectExceptionMessage($exceptionmessage); + // $this->expectException(playerException::class); + // Class for function under test. $helper = new cmi5_connectors; // Call the method under test. $test = $helper->cmi5launch_send_request_to_cmi5_player_post($testfunction, $data, $url, $filetype, $token); + // If the right message is displayed the try/catch wworked! + $this->expectOutputString($exceptionmessage); + + } + + /** + * Test the send_request_to_cmi5_player_post method with one arg. + * Test the thrown exception. + * @return void + */ + public function testcmi5launch_send_request_to_cmi5_player_post_with_one_arg_fail() + { + // We send the TEST function to the function under test now! + $testfunction = 'cmi5Test\cmi5launch_test_stream_and_send_excep'; + // Which returns the 'options' parameter passed to it. + // The player returns a string under normal circumstances. + $returnvalue = json_encode(array( + "statusCode" => 200, + "Response" => "Successful Post", + )); + + // The data to be passed to the mocked method. + $data = array( + 'actor' => array( + 'account' => array( + "homePage" => "http://testhomepage.com", + "name" => "testname", + ), + ), + 'returnUrl' => 'returnurl', + 'reg' => 'registrationid', + ); + + // Fake arguments to pass in. + $filetype = "json"; + $url = "http://test/url.com"; + + + //The arguments to pass in, in this case one, a pretend token. + $token = "testtoken"; + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong communicating with player, sending or crafting a POST request: " ; + + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + // Class for function under test. + $helper = new cmi5_connectors; + // Call the method under test. + // Note: by not sending an actual function, this will cause an exception and allow testing of try/catch and error override. + $test = $helper->cmi5launch_send_request_to_cmi5_player_post('testfunction', $data, $url, $filetype, $token); + // And the return should be a string. $this->assertIsString($test); // And it should be the same as the return value. $this->assertEquals($test, $returnvalue); } + /** * * Test the send_request_to_cmi5_player_post method with two args. * This is what is used to retrieve info like tenant info. @@ -789,12 +1238,67 @@ public function testcmi5launch_send_request_to_cmi5_player_post_with_two_args() $this->assertEquals($test, $returnvalue); } + + /** + * * Test the send_request_to_cmi5_player_post method with two args. + * This is what is used to retrieve info like tenant info. + * @return void + */ + public function testcmi5launch_send_request_to_cmi5_player_post_with_two_args_fail() + { + + // We send the TEST function to the function under test now! + $testfunction = 'cmi5Test\cmi5launch_test_stream_and_send_pass'; + + // The player returns a string under normal circumstances. + $returnvalue = json_encode(array( + "statusCode" => 200, + "Response" => "Successful Post", + )); + + // The data to be passed to the mocked method. + $data = array( + 'actor' => array( + 'account' => array( + "homePage" => "http://testhomepage.com", + "name" => "testname", + ), + ), + 'returnUrl' => 'returnurl', + 'reg' => 'registrationid', + ); + + // Fake arguments to pass in. + $filetype = "json"; + $url = "http://test/url.com"; + $contenttype = "application/json\r\n"; + + //The arguments to pass in, in this case one, a pretend username and password. + $username = "testname"; + $password = "testpassword"; + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong communicating with player, sending or crafting a POST request: " ; + + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + // Class for function under test. + $helper = new cmi5_connectors; + $test = $helper->cmi5launch_send_request_to_cmi5_player_post('testfunction', $data, $url, $filetype, $username, $password); + + + } + + /** * * Test the send_request_to_cmi5_player_get * This is what is used to retrieve info like tenant info. * @return void */ - public function testcmi5launch_send_request_to_cmi5_player_post_with_get_pass() + public function testcmi5launch_send_request_to_cmi5_player_with_get_pass() { // We send the TEST function to the function under test now! @@ -816,7 +1320,7 @@ public function testcmi5launch_send_request_to_cmi5_player_post_with_get_pass() $test = $helper->cmi5launch_send_request_to_cmi5_player_get($testfunction, $token, $url); // And the return should be an array. - $this->assertIsArray($test); + $this->assertIsString($test); // And it should be the same as the return value. $this->assertEquals($test, json_decode($returnvalue, true) ); @@ -828,10 +1332,10 @@ public function testcmi5launch_send_request_to_cmi5_player_post_with_get_pass() * This is meant to fail, we want it to act as if the player is unreachable * @return void */ - public function testcmi5launch_send_request_to_cmi5_player_post_with_get_fail() + public function testcmi5launch_send_request_to_cmi5_player_with_get_fail() { // We send the TEST function to the function under test now! - $testfunction = 'cmi5Test\cmi5launch_test_stream_and_send_fail'; + $testfunction = 'cmi5Test\cmi5launch_test_stream_and_send_excep'; // Error message for stubbed method to return. @@ -852,32 +1356,32 @@ public function testcmi5launch_send_request_to_cmi5_player_post_with_get_fail() "Accept: application/json\r\n"), ), ); + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong communicating with player, sending or crafting a GET request: " ; - // What if we run the entire class mocked except the one under test? - // would it run in the riht namespace? - // AKA OUR test namepspace - // Mock a cmi5 connector object but only stub ONE method, as we want to test the other. - + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); // This is the SUT? $helper = new cmi5_connectors; - $post = $helper->cmi5launch_get_send_request_to_cmi5_player_get(); + $get = $helper->cmi5launch_get_send_request_to_cmi5_player_get(); //$test = $mockedclass->cmi5launch_send_request_to_cmi5_player_get($token, $url); - $test = $post($testfunction, $token, $url); + $test = $get($testfunction, $token, $url); - // And the return should be an array since the method under test returns player message decoded. - $this->assertIsArray($test); - // And it should be the same as the return value. - $this->assertEquals($test, $errormessage); + } + + /** * * Test the retrieve_session method with a successful response from the player. * @return void */ - public function testcmi5launch_session_info_from_player_pass() + public function testcmi5launch_retrieve_session_info_from_player_pass() { global $cmi5launchid; @@ -894,11 +1398,11 @@ public function testcmi5launch_session_info_from_player_pass() $urltosend = $playerurl . "/api/v1/session/" . $sessionid; // The player returns a string, but the mocked method returns an array. - $returnvalue = array( + $returnvalue = json_encode(array( "statusCode" => 200, "launchMethod" => "AnyWindow", "url" => "http://testlaunchurl" - ); + )); // We send the TEST function to the function under test now! $testfunction = 'cmi5launch_stream_and_send'; @@ -913,7 +1417,7 @@ public function testcmi5launch_session_info_from_player_pass() $mockedclass->expects($this->once()) ->method('cmi5launch_send_request_to_cmi5_player_get') ->with($testfunction, $token, $urltosend) - ->willReturn(json_encode($returnvalue)); + ->willReturn(($returnvalue)); //Call the method under test. @@ -921,14 +1425,69 @@ public function testcmi5launch_session_info_from_player_pass() // And the return should be an array (the original method returns what the player sends back json-decoded or FALSE) $this->assertIsString($result); - $this->assertEquals($result, json_encode($returnvalue)); + $this->assertEquals($result,($returnvalue)); } /** * * Test the retrieve_session (launchurl) method with a failed response from the player. * @return void */ - public function testcmi5launch_session_info_from_player_fail() + public function testcmi5launch_retrieve_session_info_from_player_fail() + { + global $cmi5launchid; + + // Error message for stubbed method to return. + $errormessage = json_encode( + array("statusCode" => "404", + "error" => "Not Found", + "message" => "testmessage" )); + + + // The id to be passed to method under test. + $id = $cmi5launchid; + + //Retrieve settings like the method under test will. + $settings = cmi5launch_settings($id); + $playerurl = $settings['cmi5launchplayerurl']; + $sessionid = "testsessionid"; + $token = $settings['cmi5launchtenanttoken']; + // This is the url the stubbed method shopuld receive. + $urltosend = $playerurl . "/api/v1/session/" . $sessionid; + + // We send the TEST function to the function under test now! + $testfunction = 'cmi5launch_stream_and_send'; + + + // Mock a cmi5 connector object but only stub ONE method, as we want to test the others. + $mockedclass = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_get')) + ->getMock(); + + // Mock returns json encoded data, as it would be from the player. + $mockedclass->expects($this->once()) + ->method('cmi5launch_send_request_to_cmi5_player_get') + ->with($testfunction, $token, $urltosend) + ->willReturn($errormessage); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the session information. CMI5 Player returned 404 error. With message 'testmessage'." ; + + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result = $mockedclass->cmi5launch_retrieve_session_info_from_player($sessionid, $id); + + } + + /** + * * Test the retrieve_session (launchurl) method with a failed response from the player. + * This one tests if resulttest is false. This path shouldnt be able to be reached but is here to test the failsafe. + * @return void + */ + public function testcmi5launch_retrieve_session_info_from_player_fail_excep() { global $cmi5launchid; @@ -936,7 +1495,7 @@ public function testcmi5launch_session_info_from_player_fail() $errormessage = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); // The expected error message to be output. - $expectedstring= "
Something went wrong retrieving session info. CMI5 Player returned 404 error. With message 'testmessage'.
"; + $expectedstring= "
Something went wrong retrieving session information. CMI5 Player returned 404 error. With message 'testmessage'.
"; // The id to be passed to method under test. $id = $cmi5launchid; @@ -948,28 +1507,39 @@ public function testcmi5launch_session_info_from_player_fail() $token = $settings['cmi5launchtenanttoken']; // This is the url the stubbed method shopuld receive. $urltosend = $playerurl . "/api/v1/session/" . $sessionid; - // We send the TEST function to the function under test now! - $testfunction = 'cmi5launch_stream_and_send'; + + // We send the TEST function to the function under test now! + $testfunction = 'cmi5launch_stream_and_send'; // Mock a cmi5 connector object but only stub ONE method, as we want to test the others. $mockedclass = $this->getMockBuilder('mod_cmi5launch\local\cmi5_connectors') - ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_get')) + ->onlyMethods(array('cmi5launch_send_request_to_cmi5_player_get', 'cmi5launch_connectors_error_message')) ->getMock(); // Mock returns json encoded data, as it would be from the player. $mockedclass->expects($this->once()) ->method('cmi5launch_send_request_to_cmi5_player_get') ->with($testfunction, $token, $urltosend) - ->willReturn(json_encode($errormessage)); + ->willReturn(($errormessage)); + + // Mock returns json encoded data, as it would be from the player. + $mockedclass->expects($this->once()) + ->method('cmi5launch_connectors_error_message') + ->with($errormessage, 'retrieving the session information.') + ->willReturn(false); + + // Expected exceptions + $exceptionmessage = "Player communication error. Something went wrong retrieving the session information." ; - //Call the method under test. - $result = $mockedclass->cmi5launch_retrieve_session_info_from_player($sessionid, $id); - // Result should be debug echo string and false - $this->assertNotTrue($result, "Expected retrieved object to be false"); - //And it should output this error message - $this->expectOutputString($expectedstring); + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); + + //Call the method under test. + $result = $mockedclass->cmi5launch_retrieve_session_info_from_player($sessionid, $id); + } /** @@ -984,16 +1554,22 @@ public function testcmi5launch_connectors_error_message_path_one() $response = false; $type = "retreiving 'item'"; + $helper = new cmi5_connectors; + $error = $helper->cmi5launch_get_connectors_error_message(); + + // The expected error message to be output. - $errormessage ="
Something went wrong " . $type . ". CMI5 Player is not communicating. Is it running?
"; + $exceptionmessage ="Something went wrong " . $type . ". CMI5 Player is not communicating. Is it running?"; + + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); // Call the method under test. - $result = cmi5_connectors::cmi5launch_connectors_error_message($response, $type); + $result = $error($response, $type); - // Result should be debug echo string and false - $this->assertNotTrue($result, "Expected retrieved object to be false"); - //And it should output this error message - $this->expectOutputString($errormessage); + } /** @@ -1009,15 +1585,19 @@ public function testcmi5launch_connectors_error_message_path_two() $response = array("statusCode" => "404", "error" => "Not Found","message" => "testmessage" ); // The expected error message to be output. - $expectedstring ="
Something went wrong " . $type . ". CMI5 Player returned " . $response['statusCode'] . " error. With message '" . $response['message'] . "'.
"; + $exceptionmessage="Something went wrong " . $type . " CMI5 Player returned " . $response['statusCode'] . " error. With message '" . $response['message'] . "'."; + + $helper = new cmi5_connectors; + $error = $helper->cmi5launch_get_connectors_error_message(); + + + // Expected exceptions and messages + $this->expectExceptionMessage($exceptionmessage); + $this->expectException(playerException::class); // Call the method under test. - $result = cmi5_connectors::cmi5launch_connectors_error_message($response, $type); - - // Result should be debug echo string and false - $this->assertNotTrue($result, "Expected retrieved object to be false"); - //And it should output this error message - $this->expectOutputString($expectedstring); + $result = $error($response, $type); + } /** @@ -1036,8 +1616,11 @@ public function testcmi5launch_connectors_error_message_path_three() "Response" => "Successful Post", )); + $helper = new cmi5_connectors; + $error = $helper->cmi5launch_get_connectors_error_message(); + // Call the method under test. - $result = cmi5_connectors::cmi5launch_connectors_error_message($response, $type); + $result = $error($response, $type); // Result should be debug echo string and false $this->assertTrue($result, "Expected retrieved object to be true"); diff --git a/lib.php b/lib.php index a4ad6b4..af2db5e 100755 --- a/lib.php +++ b/lib.php @@ -619,18 +619,9 @@ function cmi5launch_process_new_package($cmi5launch) { $url = $playerurl . "/api/v1/" . $record->courseid . "/launch-url/"; $record->launchurl = $url; - // Retrieve AUs and save to table. - //TEST - try { - $aus = ($retrieveaus($test)); - } catch (Exception $e) { - echo 'Caught exception: ', $e->getMessage(), "\n"; - // stop + - } - - - //$aus = ($retrieveaus($returnedinfo)); + $aus = ($retrieveaus($returnedinfo)); $record->aus = (json_encode($aus));