diff --git a/public/repository/googledocs/tests/googledocs_search_content_test.php b/public/repository/googledocs/tests/googledocs_search_content_test.php index 3c60a23be3c..39bf8fb2a1e 100644 --- a/public/repository/googledocs/tests/googledocs_search_content_test.php +++ b/public/repository/googledocs/tests/googledocs_search_content_test.php @@ -34,16 +34,21 @@ final class googledocs_search_content_test extends \googledocs_content_testcase /** * Test get_content_nodes(). * + * @covers \repository_googledocs\googledocs_content_search * @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): void { + public function test_get_content_nodes( + string $query, + bool $sortcontent, + array $filterextensions, + array $searccontents, + array $expected + ): void { // Mock the service object. $servicemock = $this->createMock(rest::class); @@ -52,45 +57,27 @@ final class googledocs_search_content_test extends \googledocs_content_testcase 'q' => "fullText contains '" . str_replace("'", "\'", $query) . "' AND trashed = false", 'fields' => 'files(id,name,mimeType,webContentLink,webViewLink,fileExtension,modifiedTime,size,iconLink)', 'spaces' => 'drive', + 'supportsAllDrives' => 'true', + 'includeItemsFromAllDrives' => 'true', + 'corpora' => 'allDrives', ]; - 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. - $callinvocations = $this->exactly(2); + // Assert that the call() method is being called to fetch the relevant drive contents (list), + // that match the search criteria. Also, define the returned data objects by these calls. + // This simulates the more restricted drive.file scope behavior. + $callinvocations = $this->exactly(1); $servicemock->expects($callinvocations) ->method('call') ->willReturnCallback(function(string $method, array $params) use ( - $callinvocations, - $shareddrives, $searccontents, $searchparams, ) { - switch (self::getInvocationCount($callinvocations)) { - case 1: - $this->assertEquals('shared_drives_list', $method); + $this->assertEquals('list', $method); + $this->assertEquals($searchparams, $params); - $this->assertEmpty($params); - return (object) [ - 'kind' => 'drive#driveList', - 'nextPageToken' => 'd838181f30b0f5', - 'drives' => $shareddrives, - ]; - case 2: - $this->assertEquals('list', $method); - $this->assertEquals($searchparams, $params); - - return (object) [ - 'files' => $searccontents, - ]; - } + return (object) [ + 'files' => $searccontents, + ]; }); // Construct the node path. @@ -118,14 +105,11 @@ final class googledocs_search_content_test extends \googledocs_content_testcase $searchforstring = get_string('searchfor', 'repository_googledocs'); return [ - 'Folders and files match the search criteria; shared drives exist; ordering applied.' => + 'Folders and files match the search criteria; ordering applied.' => [ 'test', true, [], - [ - self::create_google_drive_shared_drive_object('d85b21c0f86cb5', 'Shared Drive 1'), - ], [ self::create_google_drive_file_object('d85b21c0f86cb0', 'Test file 3.pdf', 'application/pdf', 'pdf', '1000', '', @@ -148,12 +132,11 @@ final class googledocs_search_content_test extends \googledocs_content_testcase 'https://drive.google.com/uc?id=d85b21c0f86cb0&export=download', 'download'), ], ], - 'Only folders match the search criteria; shared drives do not exist; ordering not applied.' => + 'Only folders match the search criteria; ordering not applied.' => [ 'testing', false, [], - [], [ self::create_google_drive_folder_object('0c4ad262c65333', 'Testing folder 3'), self::create_google_drive_folder_object('d85b21c0f86cb0', 'Testing folder 1'), @@ -168,14 +151,11 @@ final class googledocs_search_content_test extends \googledocs_content_testcase "{$rootid}|Google+Drive/{$searchnodeid}|" . urlencode("{$searchforstring} 'testing'")), ], ], - 'Only files match the search criteria; shared drives exist; ordering not applied; filter .doc and .txt.' => + 'Only files match the search criteria; ordering not applied; filter .doc and .txt.' => [ 'root', false, ['doc', 'txt'], - [ - self::create_google_drive_shared_drive_object('d85b21c0f86cb5', 'Shared Drive 1'), - ], [ self::create_google_drive_file_object('d85b21c0f86cb0', 'Testing file 3.pdf', 'application/pdf', 'pdf', '1000'), @@ -190,14 +170,13 @@ final class googledocs_search_content_test extends \googledocs_content_testcase 'https://googleusercontent.com/type/application/pdf', '', 'download'), ], ], - 'No content that matches the search criteria; shared drives do not exist.' => + 'No content that matches the search criteria.' => [ 'root', false, [], [], [], - [], ], ]; } diff --git a/public/repository/googledocs/tests/local/browser/googledocs_drive_content_test.php b/public/repository/googledocs/tests/local/browser/googledocs_drive_content_test.php index 8dadb1b37d9..9258a73b408 100644 --- a/public/repository/googledocs/tests/local/browser/googledocs_drive_content_test.php +++ b/public/repository/googledocs/tests/local/browser/googledocs_drive_content_test.php @@ -16,6 +16,8 @@ namespace repository_googledocs\local\browser; +use Google_Service_Drive; + defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -53,46 +55,67 @@ final class googledocs_drive_content_test extends \googledocs_content_testcase { 'q' => "'" . str_replace("'", "\'", $query) . "' in parents AND trashed = false", 'fields' => 'files(id,name,mimeType,webContentLink,webViewLink,fileExtension,modifiedTime,size,iconLink)', 'spaces' => 'drive', + 'supportsAllDrives' => 'true', + 'includeItemsFromAllDrives' => 'true', + 'corpora' => 'allDrives', ]; - if (!empty($shareddrives)) { - $listparams['supportsAllDrives'] = 'true'; - $listparams['includeItemsFromAllDrives'] = 'true'; + if (!$this->shared_drives_supported()) { + $callinvocations = $this->exactly(1); + $servicemock->expects($callinvocations) + ->method('call') + ->willReturnCallback(function ( + string $method, + array $params + ) use ( + $listparams, + $drivecontents, + ) { + $this->assertEquals('list', $method); + $this->assertEquals($listparams, $params); + + return (object) [ + 'files' => $drivecontents, + ]; + }); + } else { + // 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. + $callinvocations = $this->exactly(2); + $servicemock->expects($callinvocations) + ->method('call') + ->willReturnCallback(function ( + string $method, + array $params + ) use ( + $callinvocations, + $shareddrives, + $listparams, + $drivecontents, + ) { + switch (self::getInvocationCount($callinvocations)) { + case 1: + $this->assertEquals('shared_drives_list', $method); + $this->assertEquals([], $params); + + return (object) [ + 'kind' => 'drive#driveList', + 'nextPageToken' => 'd838181f30b0f5', + 'drives' => $shareddrives, + ]; + case 2: + $this->assertEquals('list', $method); + $this->assertEquals($listparams, $params); + return (object)[ + 'files' => $drivecontents, + ]; + default: + $this->fail('Unexpected call to the call() method.'); + } + }); } - // 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. - $callinvocations = $this->exactly(2); - $servicemock->expects($callinvocations) - ->method('call') - ->willReturnCallback(function(string $method, array $params) use ( - $callinvocations, - $shareddrives, - $listparams, - $drivecontents, - ) { - switch (self::getInvocationCount($callinvocations)) { - case 1: - $this->assertEquals('shared_drives_list', $method); - $this->assertEquals([], $params); - - return (object) [ - 'kind' => 'drive#driveList', - 'nextPageToken' => 'd838181f30b0f5', - 'drives' => $shareddrives, - ]; - case 2: - $this->assertEquals('list', $method); - $this->assertEquals($listparams, $params); - return (object)[ - 'files' => $drivecontents, - ]; - default: - $this->fail('Unexpected call to the call() method.'); - } - }); - // Set the disallowed file types (extensions). $this->disallowedextensions = $filterextensions; $drivebrowser = new googledocs_drive_content($servicemock, $path, $sortcontent); @@ -272,4 +295,16 @@ final class googledocs_drive_content_test extends \googledocs_content_testcase { ], ]; } + + /** + * Determines whether shared drives are supported under the current Google Drive scope. + * + * @return bool + */ + private function shared_drives_supported(): bool { + $scopes = Google_Service_Drive::DRIVE_FILE; + + // Full access is needed for shared drives (not just drive.file). + return str_contains($scopes, 'auth/drive') && !str_contains($scopes, 'auth/drive.file'); + } } diff --git a/public/repository/googledocs/tests/local/browser/googledocs_root_content_test.php b/public/repository/googledocs/tests/local/browser/googledocs_root_content_test.php index 462bcb60320..32b9e178c3e 100644 --- a/public/repository/googledocs/tests/local/browser/googledocs_root_content_test.php +++ b/public/repository/googledocs/tests/local/browser/googledocs_root_content_test.php @@ -16,6 +16,8 @@ namespace repository_googledocs\local\browser; +use Google_Service_Drive; + defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -36,24 +38,43 @@ final class googledocs_root_content_test extends \googledocs_content_testcase { * @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 + * @param bool $expectshared Whether shared drives should be tested + * @covers \repository_googledocs */ - public function test_get_content_nodes(array $shareddrives, array $expected): void { + public function test_get_content_nodes(array $shareddrives, array $expected, bool $expectshared): void { + $scopessupportshared = $this->shared_drives_supported(); + + if ($expectshared && !$scopessupportshared) { + $this->markTestSkipped('Shared drives not supported in current OAuth scope.'); + } + // 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, - ]); + if ($expectshared && $scopessupportshared) { + // Expect shared drives API to be called. + // 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, + ]); + } else { + // If shared drives are not expected or not supported, call() should not be invoked. + $servicemock->expects($this->never()) + ->method('call'); + } - $rootbrowser = new googledocs_root_content($servicemock, - \repository_googledocs::REPOSITORY_ROOT_ID . '|Google+Drive', false); + $showshared = $expectshared && $scopessupportshared; + $rootbrowser = new googledocs_root_content( + $servicemock, + \repository_googledocs::REPOSITORY_ROOT_ID . '|Google+Drive', + $showshared + ); $contentnodes = $rootbrowser->get_content_nodes('', [$this, 'filter']); // Assert that the returned array of repository content nodes is equal to the expected one. @@ -84,6 +105,7 @@ final class googledocs_root_content_test extends \googledocs_content_testcase { get_string('shareddrives', 'repository_googledocs'), "{$rootid}|Google+Drive"), ], + true, // Expect shared drives. ], 'Shared drives do not exist.' => [ [], @@ -92,7 +114,20 @@ final class googledocs_root_content_test extends \googledocs_content_testcase { get_string('mydrive', 'repository_googledocs'), "{$rootid}|Google+Drive"), ], + false, // Do not expect shared drives. ], ]; } + + /** + * Determines whether shared drives are supported under the current Google Drive scope. + * + * @return bool + */ + private function shared_drives_supported(): bool { + $scopes = Google_Service_Drive::DRIVE_FILE; + + // Full access is needed for shared drives (not just drive.file). + return str_contains($scopes, 'auth/drive') && !str_contains($scopes, 'auth/drive.file'); + } }