diff --git a/repository/googledocs/tests/googledocs_content_testcase.php b/repository/googledocs/tests/googledocs_content_testcase.php new file mode 100644 index 00000000000..b097ec89f14 --- /dev/null +++ b/repository/googledocs/tests/googledocs_content_testcase.php @@ -0,0 +1,54 @@ +. + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/repository_googledocs_testcase.php'); + +/** + * Base class for the googledoc repository unit tests related to content browsing and searching. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class googledocs_content_testcase extends repository_googledocs_testcase { + + /** @var array The array which contains the disallowed file extensions. */ + protected $disallowedextensions = []; + + /** + * Method used for filtering repository file nodes based on the current disallowed file extensions list. + * + * @param array $content The repository content node + * @return bool If returns false, the repository content node should be filtered, otherwise do not filter. + */ + public function filter(array $content): bool { + // If the disallowed file extensions list is empty, do not filter the content node. + if (empty($this->disallowedextensions)) { + return true; + } + foreach ($this->disallowedextensions as $extension) { + // If the disallowed file extension matches the extension of the repository file node, + // than filter this node. + if (preg_match("#.{$extension}#i", $content['title'])) { + return false; + } + } + return true; + } +} diff --git a/repository/googledocs/tests/googledocs_search_content_test.php b/repository/googledocs/tests/googledocs_search_content_test.php new file mode 100644 index 00000000000..f0b1339f037 --- /dev/null +++ b/repository/googledocs/tests/googledocs_search_content_test.php @@ -0,0 +1,197 @@ +. + +namespace repository_googledocs; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/googledocs_content_testcase.php'); +require_once($CFG->dirroot . '/repository/googledocs/lib.php'); + +/** + * Class containing unit tests for the search content class. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class repository_googledocs_search_testcase extends \googledocs_content_testcase { + + /** + * Test get_content_nodes(). + * + * @dataProvider get_content_nodes_provider + * @param string $query The query string + * @param bool $sortcontent Whether the contents should be sorted in alphabetical order + * @param array $filterextensions The array containing file extensions that should be disallowed (filtered) + * @param array $shareddrives The array containing the existing shared drives + * @param array $searccontents The array containing the fetched google drive contents that match the search criteria + * @param array $expected The expected array which contains the generated repository content nodes + */ + public function test_get_content_nodes(string $query, bool $sortcontent, array $filterextensions, + array $shareddrives, array $searccontents, array $expected) { + + // Mock the service object. + $servicemock = $this->createMock(rest::class); + + $searchparams = [ + 'q' => "fullText contains '" . str_replace("'", "\'", $query) . "' AND trashed = false", + 'fields' => 'files(id,name,mimeType,webContentLink,webViewLink,fileExtension,modifiedTime,size,iconLink)', + 'spaces' => 'drive', + ]; + + if (!empty($shareddrives)) { + $searchparams['supportsAllDrives'] = 'true'; + $searchparams['includeItemsFromAllDrives'] = 'true'; + $searchparams['corpora'] = 'allDrives'; + } + + // Assert that the call() method is being called twice with the given arguments consecutively. In the first + // instance it is being called to fetch the shared drives (shared_drives_list), while in the second instance + // to fetch the relevant drive contents (list) that match the search criteria. Also, define the returned + // data objects by these calls. + $servicemock->expects($this->exactly(2)) + ->method('call') + ->withConsecutive( + [ + 'shared_drives_list', + [], + ], + [ + 'list', + $searchparams, + ] + ) + ->willReturnOnConsecutiveCalls( + (object)[ + 'kind' => 'drive#driveList', + 'nextPageToken' => 'd838181f30b0f5', + 'drives' => $shareddrives, + ], + (object)[ + 'files' => $searccontents, + ] + ); + + // Construct the node path. + $path = \repository_googledocs::REPOSITORY_ROOT_ID . '|' . urlencode('Google Drive') . '/' . + \repository_googledocs::SEARCH_ROOT_ID . '|' . urlencode( + get_string('searchfor', 'repository_googledocs') . " '{$query}'"); + + $searchcontentobj = new googledocs_content_search($servicemock, $path, $sortcontent); + $this->disallowedextensions = $filterextensions; + $contentnodes = $searchcontentobj->get_content_nodes($query, [$this, 'filter']); + + // Assert that the returned array of repository content nodes is equal to the expected one. + $this->assertEquals($expected, $contentnodes); + } + + /** + * Data provider for test_get_content_nodes(). + * + * @return array + */ + public function get_content_nodes_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + $searchnodeid = \repository_googledocs::SEARCH_ROOT_ID; + $searchforstring = get_string('searchfor', 'repository_googledocs'); + + return [ + 'Folders and files match the search criteria; shared drives exist; ordering applied.' => + [ + 'test', + true, + [], + [ + $this->create_google_drive_shared_drive_object('d85b21c0f86cb5', 'Shared Drive 1'), + ], + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'Test file 3.pdf', + 'application/pdf', 'pdf', '1000', '', + 'https://drive.google.com/uc?id=d85b21c0f86cb0&export=download'), + $this->create_google_drive_folder_object('0c4ad262c65333', 'Test folder 1'), + $this->create_google_drive_file_object('bed5a0f08d412a', 'Test file 1', + 'application/pdf', 'pdf'), + $this->create_google_drive_folder_object('9c4ad262c65333', 'Test folder 2'), + ], + [ + $this->create_folder_content_node_array('0c4ad262c65333', 'Test folder 1', + "{$rootid}|Google+Drive/{$searchnodeid}|" . urlencode("{$searchforstring} 'test'")), + $this->create_folder_content_node_array('9c4ad262c65333', 'Test folder 2', + "{$rootid}|Google+Drive/{$searchnodeid}|" . urlencode("{$searchforstring} 'test'")), + $this->create_file_content_node_array('bed5a0f08d412a', 'Test file 1', null, + '', 'https://googleusercontent.com/type/application/pdf', '', 'download'), + $this->create_file_content_node_array('d85b21c0f86cb0', 'Test file 3.pdf', '1000', + '', 'https://googleusercontent.com/type/application/pdf', + 'https://drive.google.com/uc?id=d85b21c0f86cb0&export=download', 'download'), + ], + ], + 'Only folders match the search criteria; shared drives do not exist; ordering not applied.' => + [ + 'testing', + false, + [], + [], + [ + $this->create_google_drive_folder_object('0c4ad262c65333', 'Testing folder 3'), + $this->create_google_drive_folder_object('d85b21c0f86cb0', 'Testing folder 1'), + $this->create_google_drive_folder_object('bed5a0f08d412a', 'Testing folder 2'), + ], + [ + $this->create_folder_content_node_array('0c4ad262c65333', 'Testing folder 3', + "{$rootid}|Google+Drive/{$searchnodeid}|" . urlencode("{$searchforstring} 'testing'")), + $this->create_folder_content_node_array('d85b21c0f86cb0', 'Testing folder 1', + "{$rootid}|Google+Drive/{$searchnodeid}|" . urlencode("{$searchforstring} 'testing'")), + $this->create_folder_content_node_array('bed5a0f08d412a', 'Testing folder 2', + "{$rootid}|Google+Drive/{$searchnodeid}|" . urlencode("{$searchforstring} 'testing'")), + ], + ], + 'Only files match the search criteria; shared drives exist; ordering not applied; filter .doc and .txt.' => + [ + 'root', + false, + ['doc', 'txt'], + [ + $this->create_google_drive_shared_drive_object('d85b21c0f86cb5', 'Shared Drive 1'), + ], + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'Testing file 3.pdf', + 'application/pdf', 'pdf', '1000'), + $this->create_google_drive_file_object('a85b21c0f86cb0', 'Testing file 1.txt', + 'text/plain', 'txt', '3000'), + $this->create_google_drive_file_object('f85b21c0f86cb0', 'Testing file 2.doc', + 'application/msword', 'doc', '2000'), + ], + [ + $this->create_file_content_node_array('d85b21c0f86cb0', 'Testing file 3.pdf', + '1000', '', 'https://googleusercontent.com/type/application/pdf', + '', 'download'), + ], + ], + 'No content that matches the search criteria; shared drives do not exist.' => + [ + 'root', + false, + [], + [], + [], + [], + ], + ]; + } +} diff --git a/repository/googledocs/tests/helper_test.php b/repository/googledocs/tests/helper_test.php new file mode 100644 index 00000000000..3b0b0900b7f --- /dev/null +++ b/repository/googledocs/tests/helper_test.php @@ -0,0 +1,281 @@ +. + +namespace repository_googledocs; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/repository_googledocs_testcase.php'); +require_once($CFG->dirroot . '/repository/googledocs/lib.php'); + +/** + * Class containing unit tests for the helper class. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class repository_googledocs_helper_testcase extends \repository_googledocs_testcase { + + /** + * Test build_node_path(). + * + * @dataProvider build_node_path_provider + * @param string $id The ID of the node + * @param string $name The name of the node + * @param string $rootpath The path to append the node on + * @param string $expected The expected node path + */ + public function test_build_node_path(string $id, string $name, string $rootpath, string $expected) { + // Assert that the returned node path is equal to the expected one. + $this->assertEquals($expected, helper::build_node_path($id, $name, $rootpath)); + } + + /** + * Data provider for test_build_node_path(). + * + * @return array + */ + public function build_node_path_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + $mydriveid = \repository_googledocs::MY_DRIVE_ROOT_ID; + $shareddrivesid = \repository_googledocs::SHARED_DRIVES_ROOT_ID; + + return [ + 'Generate the path for a node without a root path.' => + [ + $rootid, + 'Google Drive', + '', + "{$rootid}|Google+Drive", + ], + 'Generate the path for a node without a name and root path.' => + [ + $rootid, + '', + '', + $rootid, + ], + 'Generate the path for a node without a name.' => + [ + $mydriveid, + '', + "{$rootid}|Google+Drive", + "{$rootid}|Google+Drive/{$mydriveid}", + ], + 'Generate the path for a node which has a name and root path.' => + [ + '092cdf4732b9d5', + 'Shared Drive 5', + "{$rootid}|Google+Drive/{$shareddrivesid}|Shared+Drives", + "{$rootid}|Google+Drive/{$shareddrivesid}|Shared+Drives/092cdf4732b9d5|Shared+Drive+5", + ], + ]; + } + + /** + * Test explode_node_path(). + * + * @dataProvider explode_node_path_provider + * @param string $node The node string to extract information from + * @param array $expected The expected array containing the information about the node + */ + public function test_explode_node_path(string $node, array $expected) { + // Assert that the returned array is equal to the expected one. + $this->assertEquals($expected, helper::explode_node_path($node)); + } + + /** + * Data provider for test_explode_node_path(). + * + * @return array + */ + public function explode_node_path_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + + return [ + 'Return the information for a path node that has a name.' => + [ + "{$rootid}|Google+Drive", + [ + 0 => $rootid, + 1 => 'Google Drive', + 'id' => $rootid, + 'name' => 'Google Drive', + ], + ], + 'Return the information for a path node that does not have a name.' => + [ + $rootid, + [ + 0 => $rootid, + 1 => '', + 'id' => $rootid, + 'name' => '', + ], + ], + ]; + } + + /** + * Test get_browser(). + * + * @dataProvider get_browser_provider + * @param string $nodepath The node path string + * @param string $expected The expected browser class + */ + public function test_get_browser(string $nodepath, string $expected) { + // The service (rest API) object is required by get_browser(), but not being used to determine which browser + // object should be returned. Therefore, we can simply mock this object in this test. + $servicemock = $this->createMock(rest::class); + $browser = helper::get_browser($servicemock, $nodepath); + + // Assert that the returned browser class by get_browser() is equal to the expected one. + $this->assertEquals($expected, get_class($browser)); + } + + /** + * Data provider for test_get_browser(). + * + * @return array + */ + public function get_browser_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + $mydriveid = \repository_googledocs::MY_DRIVE_ROOT_ID; + $shareddrivesid = \repository_googledocs::SHARED_DRIVES_ROOT_ID; + + return [ + 'Repository root level path.' => + [ + "{$rootid}|Google+Drive", + \repository_googledocs\local\browser\googledocs_root_content::class, + ], + 'My drive path.' => + [ + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive", + \repository_googledocs\local\browser\googledocs_drive_content::class, + ], + 'Shared drives root path.' => + [ + "{$rootid}|Google+Drive/{$shareddrivesid}|Shared+Drives", + \repository_googledocs\local\browser\googledocs_shared_drives_content::class, + ], + 'Path within a shared drive.' => + [ + "{$rootid}|Google+Drive/{$shareddrivesid}|Shared+Drives/092cdf4732b9d5|Shared+Drive+5", + \repository_googledocs\local\browser\googledocs_drive_content::class, + ], + ]; + } + + /** + * Test get_node(). + * + * @dataProvider get_node_provider + * @param \stdClass $gdcontent The Google Drive content (file/folder) object + * @param string $expected The expected content node class + */ + public function test_get_node(\stdClass $gdcontent, string $expected) { + // The path is required by get_content_node(), but not being used to determine which content node + // object should be returned. Therefore, we can just generate a dummy path. + $path = \repository_googledocs::REPOSITORY_ROOT_ID . '|Google+Drive|' . + \repository_googledocs::MY_DRIVE_ROOT_ID . '|My+Drive'; + $node = helper::get_node($gdcontent, $path); + + // Assert that the returned content node class by get_node() is equal to the expected one. + $this->assertEquals($expected, get_class($node)); + } + + /** + * Data provider for test_get_node(). + * + * @return array + */ + public function get_node_provider(): array { + + return [ + 'The content object represents a Google Drive folder.' => + [ + $this->create_google_drive_folder_object('e3b0c44298fc1c149', 'Folder', ''), + \repository_googledocs\local\node\folder_node::class, + ], + 'The content object represents a Google Drive file.' => + [ + $this->create_google_drive_file_object('de04d58dc5ccc', 'File.pdf', + 'application/pdf'), + \repository_googledocs\local\node\file_node::class, + ], + ]; + } + + /** + * Test request() when an exception is thrown by the API call. + * + * @dataProvider request_exception_provider + * @param \Exception $exception The exception thrown by the API call + * @param \Exception $expected The expected exception thrown by request() + */ + public function test_request_exception(\Exception $exception, \Exception $expected) { + // Mock the service object. + $servicemock = $this->createMock(rest::class); + + // Assert that the call() method is being called only once with the given arguments. + // Define the thrown exception by this call. + $servicemock->expects($this->once()) + ->method('call') + ->with('list', []) + ->willThrowException($exception); + + $this->expectExceptionObject($expected); + + helper::request($servicemock, 'list', []); + } + + /** + * Data provider for test_request_exception(). + * + * @return array + */ + public function request_exception_provider(): array { + + return [ + 'The API call throws exception (status: 403; message: Access Not Configured).' => + [ + new \Exception('Access Not Configured', 403), + new \repository_exception('servicenotenabled', 'repository_googledocs'), + ], + 'The API call throws exception (status: 405; message: Access Not Configured).' => + [ + new \Exception('Access Not Configured', 405), + new \Exception('Access Not Configured', 405), + ], + 'The API call throws exception (status: 403; message: Access Forbidden).' => + [ + new \Exception('Access Forbidden', 403), + new \Exception('Access Forbidden', 403), + ], + 'The API call throws exception (status: 404; message: Not Found).' => + [ + new \Exception('Not Found', 404), + new \Exception('Not Found', 404), + ], + ]; + } +} diff --git a/repository/googledocs/tests/local/browser/googledocs_drive_content_test.php b/repository/googledocs/tests/local/browser/googledocs_drive_content_test.php new file mode 100644 index 00000000000..08a92c125c2 --- /dev/null +++ b/repository/googledocs/tests/local/browser/googledocs_drive_content_test.php @@ -0,0 +1,265 @@ +. + +namespace repository_googledocs\local\browser; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/googledocs_content_testcase.php'); +require_once($CFG->dirroot . '/repository/googledocs/lib.php'); + +/** + * Class containing unit tests for the drive browser class. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class repository_googledocs_drive_browser_testcase extends \googledocs_content_testcase { + + /** + * Test get_content_nodes(). + * + * @dataProvider get_content_nodes_provider + * @param string $query The query string + * @param string $path The path + * @param bool $sortcontent Whether the contents should be sorted in alphabetical order + * @param array $filterextensions The array containing file extensions that should be disallowed (filtered) + * @param array $shareddrives The array containing the existing shared drives + * @param array $drivecontents The array containing the fetched google drive contents + * @param array $expected The expected array which contains the generated repository content nodes + */ + public function test_get_content_nodes(string $query, string $path, bool $sortcontent, array $filterextensions, + array $shareddrives, array $drivecontents, array $expected) { + + // Mock the service object. + $servicemock = $this->createMock(\repository_googledocs\rest::class); + + $listparams = [ + 'q' => "'" . str_replace("'", "\'", $query) . "' in parents AND trashed = false", + 'fields' => 'files(id,name,mimeType,webContentLink,webViewLink,fileExtension,modifiedTime,size,iconLink)', + 'spaces' => 'drive', + ]; + + if (!empty($shareddrives)) { + $listparams['supportsAllDrives'] = 'true'; + $listparams['includeItemsFromAllDrives'] = 'true'; + } + + // Assert that the call() method is being called twice with the given arguments consecutively. In the first + // instance it is being called to fetch the shared drives (shared_drives_list), while in the second instance + // to fetch the relevant drive contents (list). Also, define the returned data objects by these calls. + $servicemock->expects($this->exactly(2)) + ->method('call') + ->withConsecutive( + [ + 'shared_drives_list', + [], + ], + [ + 'list', + $listparams, + ] + ) + ->willReturnOnConsecutiveCalls( + (object)[ + 'kind' => 'drive#driveList', + 'nextPageToken' => 'd838181f30b0f5', + 'drives' => $shareddrives, + ], + (object)[ + 'files' => $drivecontents, + ] + ); + + // Set the disallowed file types (extensions). + $this->disallowedextensions = $filterextensions; + $drivebrowser = new googledocs_drive_content($servicemock, $path, $sortcontent); + $contentnodes = $drivebrowser->get_content_nodes($query, [$this, 'filter']); + + // Assert that the returned array of repository content nodes is equal to the expected one. + $this->assertEquals($expected, $contentnodes); + } + + /** + * Data provider for test_get_content_nodes(). + * + * @return array + */ + public function get_content_nodes_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + $mydriveid = \repository_googledocs::MY_DRIVE_ROOT_ID; + + return [ + 'Folders and files exist in the drive; shared drives exist; ordering applied.' => + [ + $mydriveid, + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive", + true, + [], + [ + $this->create_google_drive_shared_drive_object('d85b21c0f86cb5', 'Shared Drive 1'), + ], + [ + $this->create_google_drive_folder_object('1c4ad262c65333', 'Folder 2'), + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File 3.pdf', + 'application/pdf', 'pdf', '1000'), + $this->create_google_drive_folder_object('0c4ad262c65333', 'Folder 1'), + $this->create_google_drive_file_object('bed5a0f08d412a', 'File 1', + 'application/pdf', 'pdf'), + ], + [ + $this->create_folder_content_node_array('0c4ad262c65333', 'Folder 1', + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive"), + $this->create_folder_content_node_array('1c4ad262c65333', 'Folder 2', + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive"), + $this->create_file_content_node_array('bed5a0f08d412a', 'File 1', null, '', + 'https://googleusercontent.com/type/application/pdf', '', 'download'), + $this->create_file_content_node_array('d85b21c0f86cb0', 'File 3.pdf', '1000', '', + 'https://googleusercontent.com/type/application/pdf', '', 'download'), + ], + ], + 'Only folders exist in the drive; shared drives do not exist; ordering not applied.' => + [ + $mydriveid, + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive", + false, + [], + [], + [ + $this->create_google_drive_folder_object('0c4ad262c65333', 'Folder 3'), + $this->create_google_drive_folder_object('d85b21c0f86cb0', 'Folder 1'), + $this->create_google_drive_folder_object('bed5a0f08d412a', 'Folder 2'), + ], + [ + $this->create_folder_content_node_array('0c4ad262c65333', 'Folder 3', + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive"), + $this->create_folder_content_node_array('d85b21c0f86cb0', 'Folder 1', + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive"), + $this->create_folder_content_node_array('bed5a0f08d412a', 'Folder 2', + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive"), + ], + ], + 'Only files exist in the drive; shared drives do not exist; ordering not applied; filter .txt.' => + [ + $mydriveid, + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive", + false, + ['txt'], + [], + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File 3.pdf', + 'application/pdf', 'pdf', '1000'), + $this->create_google_drive_file_object('a85b21c0f86cb0', 'File 1.txt', + 'text/plain', 'txt', '3000'), + $this->create_google_drive_file_object('f85b21c0f86cb0', 'File 2.doc', + 'application/msword', 'doc', '2000'), + ], + [ + $this->create_file_content_node_array('d85b21c0f86cb0', 'File 3.pdf', '1000', '', + 'https://googleusercontent.com/type/application/pdf', '', 'download'), + $this->create_file_content_node_array('f85b21c0f86cb0', 'File 2.doc', '2000', '', + 'https://googleusercontent.com/type/application/msword', '', 'download'), + ], + ], + 'Contents do not exist in the drive; shared drives do not exist.' => + [ + $mydriveid, + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive", + false, + [], + [], + [], + [], + ], + ]; + } + + /** + * Test get_navigation(). + * + * @dataProvider get_navigation_provider + * @param string $nodepath The node path string + * @param array $expected The expected array containing the repository navigation nodes + */ + public function test_get_navigation(string $nodepath, array $expected) { + // Mock the service object. + $servicemock = $this->createMock(\repository_googledocs\rest::class); + + $drivebrowser = new googledocs_drive_content($servicemock, $nodepath); + $navigation = $drivebrowser->get_navigation(); + + // Assert that the returned array containing the navigation nodes is equal to the expected one. + $this->assertEquals($expected, $navigation); + } + + /** + * Data provider for test_get_navigation(). + * + * @return array + */ + public function get_navigation_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + $mydriveid = \repository_googledocs::MY_DRIVE_ROOT_ID; + + return [ + 'Return navigation nodes array from path where all nodes have a name.' => + [ + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive/bed5a0f08d|Test+Folder", + [ + [ + 'name' => 'Google Drive', + 'path' => "{$rootid}|Google+Drive", + ], + [ + 'name' => 'My Drive', + 'path' => "{$rootid}|Google+Drive/{$mydriveid}|My+Drive", + ], + [ + 'name' => 'Test Folder', + 'path' => "{$rootid}|Google+Drive/{$mydriveid}|My+Drive/bed5a0f08d|Test+Folder", + ], + ], + ], + 'Return navigation nodes array from path where some nodes do not have a name.' => + [ + "{$rootid}|Google+Drive/{$mydriveid}|My+Drive/bed5a0f08d/d85b21c0f8|Test+Folder", + [ + [ + 'name' => 'Google Drive', + 'path' => "{$rootid}|Google+Drive", + ], + [ + 'name' => 'My Drive', + 'path' => "{$rootid}|Google+Drive/{$mydriveid}|My+Drive", + ], + [ + 'name' => 'bed5a0f08d', + 'path' => "{$rootid}|Google+Drive/{$mydriveid}|My+Drive/bed5a0f08d|bed5a0f08d", + ], + [ + 'name' => 'Test Folder', + 'path' => "{$rootid}|Google+Drive/{$mydriveid}|My+Drive/bed5a0f08d|bed5a0f08d/" . + "d85b21c0f8|Test+Folder", + ], + ], + ], + ]; + } +} diff --git a/repository/googledocs/tests/local/browser/googledocs_root_content_test.php b/repository/googledocs/tests/local/browser/googledocs_root_content_test.php new file mode 100644 index 00000000000..b5a2bb2112e --- /dev/null +++ b/repository/googledocs/tests/local/browser/googledocs_root_content_test.php @@ -0,0 +1,102 @@ +. + +namespace repository_googledocs\local\browser; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/googledocs_content_testcase.php'); +require_once($CFG->dirroot . '/repository/googledocs/lib.php'); + +/** + * Class containing unit tests for the repository root browser class. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class repository_googledocs_root_browser_testcase extends \googledocs_content_testcase { + + /** + * Test get_content_nodes(). + * + * @dataProvider get_content_nodes_provider + * @param array $shareddrives The array containing the existing shared drives + * @param array $expected The expected array which contains the generated repository content nodes + */ + public function test_get_content_nodes(array $shareddrives, array $expected) { + // Mock the service object. + $servicemock = $this->createMock(\repository_googledocs\rest::class); + + // Assert that the call() method is being called only once with the given arguments to fetch the existing + // shared drives. Define the returned data object by this call. + $servicemock->expects($this->once()) + ->method('call') + ->with('shared_drives_list', []) + ->willReturn((object)[ + 'kind' => 'drive#driveList', + 'nextPageToken' => 'd838181f30b0f5', + 'drives' => $shareddrives, + ]); + + $rootbrowser = new googledocs_root_content($servicemock, + \repository_googledocs::REPOSITORY_ROOT_ID . '|Google+Drive', false); + $contentnodes = $rootbrowser->get_content_nodes('', [$this, 'filter']); + + // Assert that the returned array of repository content nodes is equal to the expected one. + $this->assertEquals($expected, $contentnodes); + } + + /** + * Data provider for test_get_content_nodes(). + * + * @return array + */ + public function get_content_nodes_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + $mydriveid = \repository_googledocs::MY_DRIVE_ROOT_ID; + $shareddrivesid = \repository_googledocs::SHARED_DRIVES_ROOT_ID; + + return [ + 'Shared drives exist.' => + [ + [ + $this->create_google_drive_shared_drive_object('d85b21c0f86cb5', 'Shared Drive 1'), + $this->create_google_drive_shared_drive_object('d85b21c0f86cb0', 'Shared Drive 3'), + ], + [ + $this->create_folder_content_node_array($mydriveid, + get_string('mydrive', 'repository_googledocs'), + "{$rootid}|Google+Drive"), + $this->create_folder_content_node_array($shareddrivesid, + get_string('shareddrives', 'repository_googledocs'), + "{$rootid}|Google+Drive"), + ], + ], + 'Shared drives do not exist.' => + [ + [], + [ + $this->create_folder_content_node_array($mydriveid, + get_string('mydrive', 'repository_googledocs'), + "{$rootid}|Google+Drive"), + ], + ], + ]; + } +} diff --git a/repository/googledocs/tests/local/browser/googledocs_shared_drives_content_test.php b/repository/googledocs/tests/local/browser/googledocs_shared_drives_content_test.php new file mode 100644 index 00000000000..1c5f388c11c --- /dev/null +++ b/repository/googledocs/tests/local/browser/googledocs_shared_drives_content_test.php @@ -0,0 +1,120 @@ +. + +namespace repository_googledocs\local\browser; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/googledocs_content_testcase.php'); +require_once($CFG->dirroot . '/repository/googledocs/lib.php'); + +/** + * Class containing unit tests for the shared drives browser class. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class repository_googledocs_shared_drives_browser_testcase extends \googledocs_content_testcase { + + /** + * Test get_content_nodes(). + * + * @dataProvider get_content_nodes_provider + * @param array $shareddrives The array containing the existing shared drives + * @param bool $sortcontent Whether the contents should be sorted in alphabetical order + * @param array $expected The expected array which contains the generated repository content nodes + */ + public function test_get_content_nodes(array $shareddrives, bool $sortcontent, array $expected) { + // Mock the service object. + $servicemock = $this->createMock(\repository_googledocs\rest::class); + + // Assert that the call() method is being called only once with the given arguments to fetch the existing + // shared drives. Define the returned data object by this call. + $servicemock->expects($this->once()) + ->method('call') + ->with('shared_drives_list', []) + ->willReturn((object)[ + 'kind' => 'drive#driveList', + 'nextPageToken' => 'd838181f30b0f5', + 'drives' => $shareddrives, + ]); + + $path = \repository_googledocs::REPOSITORY_ROOT_ID . '|Google+Drive/' . + \repository_googledocs::SHARED_DRIVES_ROOT_ID . '|Shared+Drives'; + $shareddrivesbrowser = new googledocs_shared_drives_content($servicemock, $path, $sortcontent); + $contentnodes = $shareddrivesbrowser->get_content_nodes('', [$this, 'filter']); + + // Assert that the returned array of repository content nodes is equal to the expected one. + $this->assertEquals($expected, $contentnodes); + } + + /** + * Data provider for test_get_content_nodes(). + * + * @return array + */ + public function get_content_nodes_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + $shareddrivesid = \repository_googledocs::SHARED_DRIVES_ROOT_ID; + $shareddrivesstring = get_string('shareddrives', 'repository_googledocs'); + + return [ + 'Shared drives exist; ordering applied.' => + [ + [ + $this->create_google_drive_shared_drive_object('d85b21c0f86cb5', 'Shared Drive 1'), + $this->create_google_drive_shared_drive_object('d85b21c0f86cb0', 'Shared Drive 3'), + $this->create_google_drive_shared_drive_object('bed5a0f08d412a', 'Shared Drive 2'), + ], + true, + [ + $this->create_folder_content_node_array('d85b21c0f86cb5', 'Shared Drive 1', + "{$rootid}|Google+Drive/{$shareddrivesid}|" . urlencode($shareddrivesstring)), + $this->create_folder_content_node_array('bed5a0f08d412a', 'Shared Drive 2', + "{$rootid}|Google+Drive/{$shareddrivesid}|" . urlencode($shareddrivesstring)), + $this->create_folder_content_node_array('d85b21c0f86cb0', 'Shared Drive 3', + "{$rootid}|Google+Drive/{$shareddrivesid}|" . urlencode($shareddrivesstring)), + ], + ], + 'Shared drives exist; ordering not applied.' => + [ + [ + $this->create_google_drive_shared_drive_object('0c4ad262c65333', 'Shared Drive 3'), + $this->create_google_drive_shared_drive_object('d85b21c0f86cb0', 'Shared Drive 1'), + $this->create_google_drive_shared_drive_object('bed5a0f08d412a', 'Shared Drive 2'), + ], + false, + [ + $this->create_folder_content_node_array('0c4ad262c65333', 'Shared Drive 3', + "{$rootid}|Google+Drive/{$shareddrivesid}|" . urlencode($shareddrivesstring)), + $this->create_folder_content_node_array('d85b21c0f86cb0', 'Shared Drive 1', + "{$rootid}|Google+Drive/{$shareddrivesid}|" . urlencode($shareddrivesstring)), + $this->create_folder_content_node_array('bed5a0f08d412a', 'Shared Drive 2', + "{$rootid}|Google+Drive/{$shareddrivesid}|" . urlencode($shareddrivesstring)), + ], + ], + 'Shared drives do not exist.' => + [ + [], + false, + [], + ], + ]; + } +} diff --git a/repository/googledocs/tests/local/node/file_node_test.php b/repository/googledocs/tests/local/node/file_node_test.php new file mode 100644 index 00000000000..fd98de9ab99 --- /dev/null +++ b/repository/googledocs/tests/local/node/file_node_test.php @@ -0,0 +1,122 @@ +. + +namespace repository_googledocs\local\node; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/repository_googledocs_testcase.php'); + +/** + * Class containing unit tests for the repository file node class. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class file_node_testcase extends \repository_googledocs_testcase { + + /** + * Test create_node_array(). + * + * @dataProvider create_node_array_provider + * @param \stdClass $gdfile The Google Drive file object + * @param array $configsettings The googledoc repository config settings that should be set + * @param array|null $expected The expected repository file node array + */ + public function test_create_node_array(\stdClass $gdfile, array $configsettings, ?array $expected) { + $this->resetAfterTest(); + // Set the required config settings. + array_walk($configsettings, function($value, $name) { + set_config($name, $value, 'googledocs'); + }); + + $filenode = new file_node($gdfile); + $filenodearray = $filenode->create_node_array(); + // Assert that the returned repository file node array by create_node_array() is equal to the expected one. + $this->assertEquals($expected, $filenodearray); + } + + /** + * Data provider for test_create_node_array(). + * + * @return array + */ + public function create_node_array_provider(): array { + + return [ + 'Google Drive file with an extension.' => + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File.pdf', + 'application/pdf', 'pdf', '1000', '01/01/21 0:30'), + [], + $this->create_file_content_node_array('d85b21c0f86cb0', 'File.pdf', '1000', '1609432200', + 'https://googleusercontent.com/type/application/pdf', '', 'download'), + ], + 'Google Drive file that has webContentLink and webViewLink.' => + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File.pdf', + 'application/pdf', 'pdf', null, '', + 'https://drive.google.com/uc?id=d85b21c0f86cb0&export=download', + 'https://drive.google.com/file/d/d85b21c0f86cb0/view?usp=drivesdk'), + [ + 'documentformat' => 'rtf', + ], + $this->create_file_content_node_array('d85b21c0f86cb0', 'File.pdf', null, '', + 'https://googleusercontent.com/type/application/pdf', + 'https://drive.google.com/file/d/d85b21c0f86cb0/view?usp=drivesdk', 'download'), + ], + 'Google Drive file that has webContentLink and no webViewLink.' => + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File.pdf', + 'application/pdf', 'pdf', null, '', + 'https://drive.google.com/uc?id=d85b21c0f86cb0&export=download', ''), + [], + $this->create_file_content_node_array('d85b21c0f86cb0', 'File.pdf', null, '', + 'https://googleusercontent.com/type/application/pdf', + 'https://drive.google.com/uc?id=d85b21c0f86cb0&export=download', 'download'), + ], + 'Google Drive file without an extension (Google document file; documentformat config set to rtf).' => + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File', + 'application/vnd.google-apps.document', null), + [ + 'documentformat' => 'rtf', + ], + $this->create_file_content_node_array('d85b21c0f86cb0', 'File.gdoc', '', '', + 'https://googleusercontent.com/type/application/vnd.google-apps.document', '', + 'application/rtf'), + ], + 'Google Drive file without an extension (Google presentation file; presentationformat config not set).' => + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File', + 'application/vnd.google-apps.presentation', null), + [ + 'documentformat' => 'rtf', + ], + null, + ], + 'Google Drive file without an extension (File type not supported).' => + [ + $this->create_google_drive_file_object('d85b21c0f86cb0', 'File', + 'application/vnd.google-apps.unknownmimetype', null), + [], + null, + ], + ]; + } +} diff --git a/repository/googledocs/tests/local/node/folder_node_test.php b/repository/googledocs/tests/local/node/folder_node_test.php new file mode 100644 index 00000000000..474da7c3630 --- /dev/null +++ b/repository/googledocs/tests/local/node/folder_node_test.php @@ -0,0 +1,73 @@ +. + +namespace repository_googledocs\local\node; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/repository/googledocs/tests/repository_googledocs_testcase.php'); + +/** + * Class containing unit tests for the repository folder node class. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class folder_node_testcase extends \repository_googledocs_testcase { + + /** + * Test create_node_array(). + * + * @dataProvider create_node_array_provider + * @param \stdClass $gdfolder The Google Drive folder object + * @param string $path The current path + * @param array $expected The expected repository folder node array + */ + public function test_create_node_array(\stdClass $gdfolder, string $path, array $expected) { + $foldernode = new folder_node($gdfolder, $path); + $foldernodearray = $foldernode->create_node_array(); + // Assert that the returned repository folder node array by create_node_array() is equal to the expected one. + $this->assertEquals($expected, $foldernodearray); + } + + /** + * Data provider for test_create_node_array(). + * + * @return array + */ + public function create_node_array_provider(): array { + + $rootid = \repository_googledocs::REPOSITORY_ROOT_ID; + + return [ + 'Google Drive folder with modified date.' => + [ + $this->create_google_drive_folder_object('d85b21c0f86cb0', 'Folder', '01/01/21 0:30'), + "{$rootid}|Google+Drive", + $this->create_folder_content_node_array('d85b21c0f86cb0', 'Folder', + "{$rootid}|Google+Drive", '1609432200'), + ], + 'Google Drive folder without modified date.' => + [ + $this->create_google_drive_folder_object('d85b21c0f86cb0', 'Folder', ''), + '', + $this->create_folder_content_node_array('d85b21c0f86cb0', 'Folder', '', ''), + ], + ]; + } +} diff --git a/repository/googledocs/tests/repository_googledocs_testcase.php b/repository/googledocs/tests/repository_googledocs_testcase.php new file mode 100644 index 00000000000..9bb325d5f44 --- /dev/null +++ b/repository/googledocs/tests/repository_googledocs_testcase.php @@ -0,0 +1,152 @@ +. + +/** + * Base class for the googledoc repository unit tests. + * + * @package repository_googledocs + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class repository_googledocs_testcase extends \advanced_testcase { + + /** + * Create an array that would replicate the structure of a repository folder node array. + * + * @param string $id The ID of the folder + * @param string $name The name of the folder + * @param string $path The root path + * @param string $modified The date of the last modification + * @return array The repository folder node array + */ + protected function create_folder_content_node_array(string $id, string $name, string $path, + string $modified = ''): array { + + global $OUTPUT; + + return [ + 'id' => $id, + 'title' => $name, + 'path' => repository_googledocs\helper::build_node_path($id, $name, $path), + 'date' => $modified, + 'thumbnail' => $OUTPUT->image_url(file_folder_icon(64))->out(false), + 'thumbnail_height' => 64, + 'thumbnail_width' => 64, + 'children' => [], + ]; + } + + /** + * Create an array that would replicate the structure of a repository file node array. + * + * @param string $id The ID of the file + * @param string $name The name of the file + * @param string|null $size The size of the file + * @param string $modified The date of the last modification + * @param string $thumbnail The thumbnail of the file + * @param string|null $link The external link to the file + * @param string|null $exportformat The export format of the file + * @return array The repository file node array + */ + protected function create_file_content_node_array(string $id, string $name, ?string $size = null, + string $modified = '', string $thumbnail = '' , string $link = '', string $exportformat = ''): array { + + return [ + 'id' => $id, + 'title' => $name, + 'source' => json_encode([ + 'id' => $id, + 'name' => $name, + 'link' => $link, + 'exportformat' => $exportformat + ]), + 'date' => $modified, + 'size' => $size, + 'thumbnail' => $thumbnail, + 'thumbnail_height' => 64, + 'thumbnail_width' => 64, + ]; + } + + /** + * Create an object that would replicate the metadata for a shared drive returned by the Google Drive API call. + * + * @param string $id The ID of the shared drive + * @param string $name The name of the shared drive + * @return \stdClass The shared drive object + */ + protected function create_google_drive_shared_drive_object(string $id, string $name): \stdClass { + return (object)[ + 'kind' => 'drive#drive', + 'id' => $id, + 'name' => $name, + ]; + } + + /** + * Create an object that would replicate the metadata for a folder returned by the Google Drive API call. + * + * @param string $id The ID of the folder + * @param string $name The name of the folder + * @param string|null $modified The date of the last modification + * @return \stdClass The folder object + */ + protected function create_google_drive_folder_object(string $id, string $name, ?string $modified = null): \stdClass { + return (object)[ + 'id' => $id, + 'name' => $name, + 'mimeType' => 'application/vnd.google-apps.folder', + 'webViewLink' => "https://drive.google.com/drive/folders/{$id}", + 'iconLink' => 'https://googleusercontent.com/16/type/application/vnd.google-apps.folder+shared', + 'modifiedTime' => $modified ?? '', + ]; + } + + /** + * Create an object that would replicate the metadata for a file returned by the Google Drive API call. + * + * @param string $id The ID of the file + * @param string $name The name of the file + * @param string $mimetype The mimetype of the file + * @param string|null $extension The extension of the file + * @param string|null $size The size of the file + * @param string|null $modified The date of the last modification + * @param string|null $webcontentlink The link for downloading the content of the file + * @param string|null $webviewlink The link for opening the file in a browser + * @return \stdClass The file object + */ + protected function create_google_drive_file_object(string $id, string $name, string $mimetype, + ?string $extension = null, ?string $size = null, ?string $modified = null, ?string $webcontentlink = null, + ?string $webviewlink = null): \stdClass { + + $googledrivefile = [ + 'id' => $id, + 'name' => $name, + 'mimeType' => $mimetype, + 'size' => $size ?? '', + 'webContentLink' => $webcontentlink ?? '', + 'webViewLink' => $webviewlink ?? '', + 'iconLink' => "https://googleusercontent.com/type/{$mimetype}", + 'modifiedTime' => $modified ?? '', + ]; + // The returned metadata might not always have the 'fileExtension' property. + if ($extension) { + $googledrivefile['fileExtension'] = $extension; + } + + return (object)$googledrivefile; + } +}