Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 9 May 2022 (12 months).
  • Bug fixes for security issues in 3.11.x will end 14 November 2022 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
  • Differences Between: [Versions 36 and 311] [Versions 37 and 311] [Versions 38 and 311] [Versions 39 and 311]

       1  <?php
       2  // This file is part of Moodle - http://moodle.org/
       3  //
       4  // Moodle is free software: you can redistribute it and/or modify
       5  // it under the terms of the GNU General Public License as published by
       6  // the Free Software Foundation, either version 3 of the License, or
       7  // (at your option) any later version.
       8  //
       9  // Moodle is distributed in the hope that it will be useful,
      10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12  // GNU General Public License for more details.
      13  //
      14  // You should have received a copy of the GNU General Public License
      15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
      16  
      17  /**
      18   * This file contains tests for the repository_nextcloud class.
      19   *
      20   * @package     repository_nextcloud
      21   * @copyright  2017 Project seminar (Learnweb, University of Münster)
      22   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      23   */
      24  
      25  defined('MOODLE_INTERNAL') || die();
      26  
      27  global $CFG;
      28  require_once($CFG->dirroot . '/repository/lib.php');
      29  require_once($CFG->libdir . '/webdavlib.php');
      30  
      31  /**
      32   * Class repository_nextcloud_lib_testcase
      33   * @group repository_nextcloud
      34   * @copyright  2017 Project seminar (Learnweb, University of Münster)
      35   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      36   */
      37  class repository_nextcloud_lib_testcase extends advanced_testcase {
      38  
      39      /** @var null|\repository_nextcloud the repository_nextcloud object, which the tests are run on. */
      40      private $repo = null;
      41  
      42      /** @var null|\core\oauth2\issuer which belongs to the repository_nextcloud object.*/
      43      private $issuer = null;
      44  
      45      /**
      46       * SetUp to create an repository instance.
      47       */
      48      protected function setUp(): void {
      49          $this->resetAfterTest(true);
      50  
      51          // Admin is neccessary to create api and issuer objects.
      52          $this->setAdminUser();
      53  
      54          /** @var repository_nextcloud_generator $generator */
      55          $generator = $this->getDataGenerator()->get_plugin_generator('repository_nextcloud');
      56          $this->issuer = $generator->test_create_issuer();
      57  
      58          // Create Endpoints for issuer.
      59          $generator->test_create_endpoints($this->issuer->get('id'));
      60  
      61          // Params for the config form.
      62          $reptype = $generator->create_type([
      63              'visible' => 1,
      64              'enableuserinstances' => 0,
      65              'enablecourseinstances' => 0,
      66          ]);
      67  
      68          $instance = $generator->create_instance([
      69              'issuerid' => $this->issuer->get('id'),
      70              'pluginname' => 'Nextcloud',
      71              'controlledlinkfoldername' => 'Moodlefiles',
      72              'supportedreturntypes' => 'both',
      73              'defaultreturntype' => FILE_INTERNAL,
      74          ]);
      75  
      76          // At last, create a repository_nextcloud object from the instance id.
      77          $this->repo = new repository_nextcloud($instance->id);
      78          $this->repo->options['typeid'] = $reptype->id;
      79          $this->repo->options['sortorder'] = 1;
      80          $this->resetAfterTest(true);
      81      }
      82  
      83      /**
      84       * Checks the is_visible method in case the repository is set to hidden in the database.
      85       */
      86      public function test_is_visible_parent_false() {
      87          global $DB;
      88          $id = $this->repo->options['typeid'];
      89  
      90          // Check, if the method returns false, when the repository is set to visible in the database
      91          // and the client configuration data is complete.
      92          $DB->update_record('repository', (object) array('id' => $id, 'visible' => 0));
      93  
      94          $this->assertFalse($this->repo->is_visible());
      95      }
      96  
      97      /**
      98       * Test whether the repo is disabled.
      99       */
     100      public function test_repo_creation() {
     101          $issuerid = $this->repo->get_option('issuerid');
     102  
     103          // Config saves the right id.
     104          $this->assertEquals($this->issuer->get('id'), $issuerid);
     105  
     106          // Function that is used in construct method returns the right id.
     107          $constructissuer = \core\oauth2\api::get_issuer($issuerid);
     108          $this->assertEquals($this->issuer->get('id'), $constructissuer->get('id'));
     109  
     110          $this->assertEquals(true, $constructissuer->get('enabled'));
     111          $this->assertFalse($this->repo->disabled);
     112      }
     113  
     114      /**
     115       * Returns an array of endpoints or null.
     116       * @param string $endpointname
     117       * @return array|null
     118       */
     119      private function get_endpoint_id($endpointname) {
     120          $endpoints = \core\oauth2\api::get_endpoints($this->issuer);
     121          $id = array();
     122          foreach ($endpoints as $endpoint) {
     123              $name = $endpoint->get('name');
     124              if ($name === $endpointname) {
     125                  $id[$endpoint->get('id')] = $endpoint->get('id');
     126              }
     127          }
     128          if (empty($id)) {
     129              return null;
     130          }
     131          return $id;
     132      }
     133      /**
     134       * Test if repository is disabled when webdav_endpoint is deleted.
     135       */
     136      public function test_issuer_webdav() {
     137          $idwebdav = $this->get_endpoint_id('webdav_endpoint');
     138          if (!empty($idwebdav)) {
     139              foreach ($idwebdav as $id) {
     140                  \core\oauth2\api::delete_endpoint($id);
     141              }
     142          }
     143          $this->assertFalse(\repository_nextcloud\issuer_management::is_valid_issuer($this->issuer));
     144      }
     145      /**
     146       * Test if repository is disabled when ocs_endpoint is deleted.
     147       */
     148      public function test_issuer_ocs() {
     149          $idocs = $this->get_endpoint_id('ocs_endpoint');
     150          if (!empty($idocs)) {
     151              foreach ($idocs as $id) {
     152                  \core\oauth2\api::delete_endpoint($id);
     153              }
     154          }
     155          $this->assertFalse(\repository_nextcloud\issuer_management::is_valid_issuer($this->issuer));
     156      }
     157  
     158      /**
     159       * Test if repository is disabled when userinfo_endpoint is deleted.
     160       */
     161      public function test_issuer_userinfo() {
     162          $idtoken = $this->get_endpoint_id('userinfo_endpoint');
     163          if (!empty($idtoken)) {
     164              foreach ($idtoken as $id) {
     165                  \core\oauth2\api::delete_endpoint($id);
     166              }
     167          }
     168          $this->assertFalse(\repository_nextcloud\issuer_management::is_valid_issuer($this->issuer));
     169      }
     170  
     171      /**
     172       * Test if repository is disabled when token_endpoint is deleted.
     173       */
     174      public function test_issuer_token() {
     175          $idtoken = $this->get_endpoint_id('token_endpoint');
     176          if (!empty($idtoken)) {
     177              foreach ($idtoken as $id) {
     178                  \core\oauth2\api::delete_endpoint($id);
     179              }
     180          }
     181          $this->assertFalse(\repository_nextcloud\issuer_management::is_valid_issuer($this->issuer));
     182      }
     183  
     184      /**
     185       * Test if repository is disabled when auth_endpoint is deleted.
     186       */
     187      public function test_issuer_authorization() {
     188          $idauth = $this->get_endpoint_id('authorization_endpoint');
     189          if (!empty($idauth)) {
     190              foreach ($idauth as $id) {
     191                  \core\oauth2\api::delete_endpoint($id);
     192              }
     193          }
     194          $this->assertFalse(\repository_nextcloud\issuer_management::is_valid_issuer($this->issuer));
     195      }
     196      /**
     197       * Test if repository throws an error when endpoint does not exist.
     198       */
     199      public function test_parse_endpoint_url_error() {
     200          $this->expectException(\repository_nextcloud\configuration_exception::class);
     201          \repository_nextcloud\issuer_management::parse_endpoint_url('notexisting', $this->issuer);
     202      }
     203      /**
     204       * Test get_listing method with an example directory. Tests error cases.
     205       */
     206      public function test_get_listing_error() {
     207          $ret = $this->get_initialised_return_array();
     208          $this->setUser();
     209          // WebDAV socket is not opened.
     210          $mock = $this->createMock(\webdav_client::class);
     211          $mock->expects($this->once())->method('open')->will($this->returnValue(false));
     212          $private = $this->set_private_property($mock, 'dav');
     213  
     214          $this->assertEquals($ret, $this->repo->get_listing('/'));
     215  
     216          // Response is not an array.
     217          $mock = $this->createMock(\webdav_client::class);
     218          $mock->expects($this->once())->method('open')->will($this->returnValue(true));
     219          $mock->expects($this->once())->method('ls')->will($this->returnValue('notanarray'));
     220          $private->setValue($this->repo, $mock);
     221  
     222          $this->assertEquals($ret, $this->repo->get_listing('/'));
     223      }
     224      /**
     225       * Test get_listing method with an example directory. Tests the root directory.
     226       */
     227      public function test_get_listing_root() {
     228          $this->setUser();
     229          $ret = $this->get_initialised_return_array();
     230  
     231          // This is the expected response from the ls method.
     232          $response = array(
     233              array(
     234                  'href' => 'remote.php/webdav/',
     235                  'lastmodified' => 'Thu, 08 Dec 2016 16:06:26 GMT',
     236                  'resourcetype' => 'collection',
     237                  'status' => 'HTTP/1.1 200 OK',
     238                  'getcontentlength' => ''
     239              ),
     240              array(
     241                  'href' => 'remote.php/webdav/Documents/',
     242                  'lastmodified' => 'Thu, 08 Dec 2016 16:06:26 GMT',
     243                  'resourcetype' => 'collection',
     244                  'status' => 'HTTP/1.1 200 OK',
     245                  'getcontentlength' => ''
     246              ),
     247              array(
     248                  'href' => 'remote.php/webdav/welcome.txt',
     249                  'lastmodified' => 'Thu, 08 Dec 2016 16:06:26 GMT',
     250                  'status' => 'HTTP/1.1 200 OK',
     251                  'getcontentlength' => '163'
     252              )
     253          );
     254  
     255          // The expected result from the get_listing method in the repository_nextcloud class.
     256          $ret['list'] = array(
     257              'DOCUMENTS/' => array(
     258                  'title' => 'Documents',
     259                  'thumbnail' => null,
     260                  'children' => array(),
     261                  'datemodified' => 1481213186,
     262                  'path' => '/Documents/'
     263              ),
     264              'WELCOME.TXT' => array(
     265                  'title' => 'welcome.txt',
     266                  'thumbnail' => null,
     267                  'size' => '163',
     268                  'datemodified' => 1481213186,
     269                  'source' => '/welcome.txt'
     270              )
     271          );
     272  
     273          // Valid response from the client.
     274          $mock = $this->createMock(\webdav_client::class);
     275          $mock->expects($this->once())->method('open')->will($this->returnValue(true));
     276          $mock->expects($this->once())->method('ls')->will($this->returnValue($response));
     277          $this->set_private_property($mock, 'dav');
     278  
     279          $ls = $this->repo->get_listing('/');
     280  
     281          // Those attributes can not be tested properly.
     282          $ls['list']['DOCUMENTS/']['thumbnail'] = null;
     283          $ls['list']['WELCOME.TXT']['thumbnail'] = null;
     284  
     285          $this->assertEquals($ret, $ls);
     286      }
     287      /**
     288       * Test get_listing method with an example directory. Tests a different directory than the root
     289       * directory.
     290       */
     291      public function test_get_listing_directory() {
     292          $ret = $this->get_initialised_return_array();
     293          $this->setUser();
     294  
     295          // An additional directory path has to be added to the 'path' field within the returned array.
     296          $ret['path'][1] = array(
     297              'name' => 'dir',
     298              'path' => '/dir/'
     299          );
     300  
     301          // This is the expected response from the get_listing method in the Nextcloud client.
     302          $response = array(
     303              array(
     304                  'href' => 'remote.php/webdav/dir/',
     305                  'lastmodified' => 'Thu, 08 Dec 2016 16:06:26 GMT',
     306                  'resourcetype' => 'collection',
     307                  'status' => 'HTTP/1.1 200 OK',
     308                  'getcontentlength' => ''
     309              ),
     310              array(
     311                  'href' => 'remote.php/webdav/dir/Documents/',
     312                  'lastmodified' => null,
     313                  'resourcetype' => 'collection',
     314                  'status' => 'HTTP/1.1 200 OK',
     315                  'getcontentlength' => ''
     316              ),
     317              array(
     318                  'href' => 'remote.php/webdav/dir/welcome.txt',
     319                  'lastmodified' => 'Thu, 08 Dec 2016 16:06:26 GMT',
     320                  'status' => 'HTTP/1.1 200 OK',
     321                  'getcontentlength' => '163'
     322              )
     323          );
     324  
     325          // The expected result from the get_listing method in the repository_nextcloud class.
     326          $ret['list'] = array(
     327              'DOCUMENTS/' => array(
     328                  'title' => 'Documents',
     329                  'thumbnail' => null,
     330                  'children' => array(),
     331                  'datemodified' => null,
     332                  'path' => '/dir/Documents/'
     333              ),
     334              'WELCOME.TXT' => array(
     335                  'title' => 'welcome.txt',
     336                  'thumbnail' => null,
     337                  'size' => '163',
     338                  'datemodified' => 1481213186,
     339                  'source' => '/dir/welcome.txt'
     340              )
     341          );
     342  
     343          // Valid response from the client.
     344          $mock = $this->createMock(\webdav_client::class);
     345          $mock->expects($this->once())->method('open')->will($this->returnValue(true));
     346          $mock->expects($this->once())->method('ls')->will($this->returnValue($response));
     347          $this->set_private_property($mock, 'dav');
     348  
     349          $ls = $this->repo->get_listing('/dir/');
     350  
     351          // Can not be tested properly.
     352          $ls['list']['DOCUMENTS/']['thumbnail'] = null;
     353          $ls['list']['WELCOME.TXT']['thumbnail'] = null;
     354  
     355          $this->assertEquals($ret, $ls);
     356      }
     357      /**
     358       * Test the get_link method.
     359       */
     360      public function test_get_link_success() {
     361          $mock = $this->getMockBuilder(\repository_nextcloud\ocs_client::class)->disableOriginalConstructor()->disableOriginalClone(
     362              )->getMock();
     363          $file = '/datei';
     364          $expectedresponse = <<<XML
     365  <?xml version="1.0"?>
     366  <ocs>
     367   <meta>
     368    <status>ok</status>
     369    <statuscode>100</statuscode>
     370    <message/>
     371   </meta>
     372   <data>
     373    <id>2</id>
     374    <share_type>3</share_type>
     375    <uid_owner>admin</uid_owner>
     376    <displayname_owner>admin</displayname_owner>
     377    <permissions>1</permissions>
     378    <stime>1502883721</stime>
     379    <parent/>
     380    <expiration/>
     381    <token>QXbqrJj8DcMaXen</token>
     382    <uid_file_owner>admin</uid_file_owner>
     383    <displayname_file_owner>admin</displayname_file_owner>
     384    <path>/somefile</path>
     385    <item_type>file</item_type>
     386    <mimetype>application/pdf</mimetype>
     387    <storage_id>home::admin</storage_id>
     388    <storage>1</storage>
     389    <item_source>6</item_source>
     390    <file_source>6</file_source>
     391    <file_parent>4</file_parent>
     392    <file_target>/somefile</file_target>
     393    <share_with/>
     394    <share_with_displayname/>
     395    <name/>
     396    <url>https://www.default.test/somefile</url>
     397    <mail_send>0</mail_send>
     398   </data>
     399  </ocs>
     400  XML;
     401          // Expected Parameters.
     402          $ocsquery = [
     403              'path' => $file,
     404              'shareType' => \repository_nextcloud\ocs_client::SHARE_TYPE_PUBLIC,
     405              'publicUpload' => false,
     406              'permissions' => \repository_nextcloud\ocs_client::SHARE_PERMISSION_READ
     407          ];
     408  
     409          // With test whether mock is called with right parameters.
     410          $mock->expects($this->once())->method('call')->with('create_share', $ocsquery)->will($this->returnValue($expectedresponse));
     411          $this->set_private_property($mock, 'ocsclient');
     412  
     413          // Method does extract the link from the xml format.
     414          $this->assertEquals('https://www.default.test/somefile/download', $this->repo->get_link($file));
     415      }
     416  
     417      /**
     418       * get_link can get OCS failure responses. Test that this is handled appropriately.
     419       */
     420      public function test_get_link_failure() {
     421          $mock = $this->getMockBuilder(\repository_nextcloud\ocs_client::class)->disableOriginalConstructor()->disableOriginalClone(
     422              )->getMock();
     423          $file = '/datei';
     424          $expectedresponse = <<<XML
     425  <?xml version="1.0"?>
     426  <ocs>
     427   <meta>
     428    <status>failure</status>
     429    <statuscode>404</statuscode>
     430    <message>Msg</message>
     431   </meta>
     432   <data/>
     433  </ocs>
     434  XML;
     435          // Expected Parameters.
     436          $ocsquery = [
     437              'path' => $file,
     438              'shareType' => \repository_nextcloud\ocs_client::SHARE_TYPE_PUBLIC,
     439              'publicUpload' => false,
     440              'permissions' => \repository_nextcloud\ocs_client::SHARE_PERMISSION_READ
     441          ];
     442  
     443          // With test whether mock is called with right parameters.
     444          $mock->expects($this->once())->method('call')->with('create_share', $ocsquery)->will($this->returnValue($expectedresponse));
     445          $this->set_private_property($mock, 'ocsclient');
     446  
     447          // Suppress (expected) XML parse error... Nextcloud sometimes returns JSON on extremely bad errors.
     448          libxml_use_internal_errors(true);
     449  
     450          // Method get_link correctly raises an exception that contains error code and message.
     451          $this->expectException(\repository_nextcloud\request_exception::class);
     452          $params = array('instance' => $this->repo->get_name(), 'errormessage' => sprintf('(%s) %s', '404', 'Msg'));
     453          $this->expectExceptionMessage(get_string('request_exception', 'repository_nextcloud', $params));
     454          $this->repo->get_link($file);
     455      }
     456  
     457      /**
     458       * get_link can get OCS responses that are not actually XML. Test that this is handled appropriately.
     459       */
     460      public function test_get_link_problem() {
     461          $mock = $this->getMockBuilder(\repository_nextcloud\ocs_client::class)->disableOriginalConstructor()->disableOriginalClone(
     462              )->getMock();
     463          $file = '/datei';
     464          $expectedresponse = <<<JSON
     465  {"message":"CSRF check failed"}
     466  JSON;
     467          // Expected Parameters.
     468          $ocsquery = [
     469              'path' => $file,
     470              'shareType' => \repository_nextcloud\ocs_client::SHARE_TYPE_PUBLIC,
     471              'publicUpload' => false,
     472              'permissions' => \repository_nextcloud\ocs_client::SHARE_PERMISSION_READ
     473          ];
     474  
     475          // With test whether mock is called with right parameters.
     476          $mock->expects($this->once())->method('call')->with('create_share', $ocsquery)->will($this->returnValue($expectedresponse));
     477          $this->set_private_property($mock, 'ocsclient');
     478  
     479          // Suppress (expected) XML parse error... Nextcloud sometimes returns JSON on extremely bad errors.
     480          libxml_use_internal_errors(true);
     481  
     482          // Method get_link correctly raises an exception.
     483          $this->expectException(\repository_nextcloud\request_exception::class);
     484          $this->repo->get_link($file);
     485      }
     486  
     487      /**
     488       * Test get_file reference, merely returns the input if no optional_param is set.
     489       */
     490      public function test_get_file_reference_withoutoptionalparam() {
     491          $this->assertEquals('/somefile', $this->repo->get_file_reference('/somefile'));
     492      }
     493  
     494      /**
     495       * Test logout.
     496       */
     497      public function test_logout() {
     498          $mock = $this->createMock(\core\oauth2\client::class);
     499  
     500          $mock->expects($this->exactly(2))->method('log_out');
     501          $this->set_private_property($mock, 'client');
     502          $this->repo->options['ajax'] = false;
     503          $this->expectOutputString('<a target="_blank" rel="noopener noreferrer">Log in to your account</a>' .
     504              '<a target="_blank" rel="noopener noreferrer">Log in to your account</a>');
     505  
     506          $this->assertEquals($this->repo->print_login(), $this->repo->logout());
     507  
     508          $mock->expects($this->exactly(2))->method('get_login_url')->will($this->returnValue(new moodle_url('url')));
     509  
     510          $this->repo->options['ajax'] = true;
     511          $this->assertEquals($this->repo->print_login(), $this->repo->logout());
     512  
     513      }
     514      /**
     515       * Test for the get_file method from the repository_nextcloud class.
     516       */
     517      public function test_get_file() {
     518          // WebDAV socket is not open.
     519          $mock = $this->createMock(\webdav_client::class);
     520          $mock->expects($this->once())->method('open')->will($this->returnValue(false));
     521          $private = $this->set_private_property($mock, 'dav');
     522  
     523          $this->assertFalse($this->repo->get_file('path'));
     524  
     525          // WebDAV socket is open and the request successful.
     526          $mock = $this->createMock(\webdav_client::class);
     527          $mock->expects($this->once())->method('open')->will($this->returnValue(true));
     528          $mock->expects($this->once())->method('get_file')->will($this->returnValue(true));
     529          $private->setValue($this->repo, $mock);
     530  
     531          $result = $this->repo->get_file('path', 'file');
     532  
     533          $this->assertNotNull($result['path']);
     534      }
     535  
     536      /**
     537       * Test callback.
     538       */
     539      public function test_callback() {
     540          $mock = $this->createMock(\core\oauth2\client::class);
     541          // Should call check_login exactly once.
     542          $mock->expects($this->once())->method('log_out');
     543          $mock->expects($this->once())->method('is_logged_in');
     544  
     545          $this->set_private_property($mock, 'client');
     546  
     547          $this->repo->callback();
     548      }
     549      /**
     550       * Test check_login.
     551       */
     552      public function test_check_login() {
     553          $mock = $this->createMock(\core\oauth2\client::class);
     554          $mock->expects($this->once())->method('is_logged_in')->will($this->returnValue(true));
     555          $this->set_private_property($mock, 'client');
     556  
     557          $this->assertTrue($this->repo->check_login());
     558      }
     559      /**
     560       * Test print_login.
     561       */
     562      public function test_print_login() {
     563          $mock = $this->createMock(\core\oauth2\client::class);
     564          $mock->expects($this->exactly(2))->method('get_login_url')->will($this->returnValue(new moodle_url('url')));
     565          $this->set_private_property($mock, 'client');
     566  
     567          // Test with ajax activated.
     568          $this->repo->options['ajax'] = true;
     569  
     570          $url = new moodle_url('url');
     571          $ret = array();
     572          $btn = new \stdClass();
     573          $btn->type = 'popup';
     574          $btn->url = $url->out(false);
     575          $ret['login'] = array($btn);
     576  
     577          $this->assertEquals($ret, $this->repo->print_login());
     578  
     579          // Test without ajax.
     580          $this->repo->options['ajax'] = false;
     581  
     582          $output = html_writer::link($url, get_string('login', 'repository'),
     583              array('target' => '_blank',  'rel' => 'noopener noreferrer'));
     584          $this->expectOutputString($output);
     585          $this->repo->print_login();
     586      }
     587  
     588      /**
     589       * Test the initiate_webdavclient function.
     590       */
     591      public function test_initiate_webdavclient() {
     592          global $CFG;
     593  
     594          $idwebdav = $this->get_endpoint_id('webdav_endpoint');
     595          if (!empty($idwebdav)) {
     596              foreach ($idwebdav as $id) {
     597                  \core\oauth2\api::delete_endpoint($id);
     598              }
     599          }
     600  
     601          $generator = $this->getDataGenerator()->get_plugin_generator('repository_nextcloud');
     602          $generator->test_create_single_endpoint($this->issuer->get('id'), "webdav_endpoint",
     603              "https://www.default.test:8080/webdav/index.php");
     604  
     605          $fakeaccesstoken = new stdClass();
     606          $fakeaccesstoken->token = "fake access token";
     607          $oauthmock = $this->createMock(\core\oauth2\client::class);
     608          $oauthmock->expects($this->once())->method('get_accesstoken')->will($this->returnValue($fakeaccesstoken));
     609          $this->set_private_property($oauthmock, 'client');
     610  
     611          $dav = phpunit_util::call_internal_method($this->repo, "initiate_webdavclient", [], 'repository_nextcloud');
     612  
     613          // Verify that port is set correctly (private property).
     614          $refclient = new ReflectionClass($dav);
     615  
     616          $property = $refclient->getProperty('_port');
     617          $property->setAccessible(true);
     618  
     619          $port = $property->getValue($dav);
     620  
     621          $this->assertEquals('8080', $port);
     622      }
     623  
     624      /**
     625       * Test supported_returntypes.
     626       * FILE_INTERNAL when no system account is connected.
     627       * FILE_INTERNAL | FILE_CONTROLLED_LINK when a system account is connected.
     628       */
     629      public function test_supported_returntypes() {
     630          global $DB;
     631          $this->assertEquals(FILE_INTERNAL, $this->repo->supported_returntypes());
     632          $dataobject = new stdClass();
     633          $dataobject->timecreated = time();
     634          $dataobject->timemodified = time();
     635          $dataobject->usermodified = 2;
     636          $dataobject->issuerid = $this->issuer->get('id');
     637          $dataobject->refreshtoken = 'sometokenthatwillnotbeused';
     638          $dataobject->grantedscopes = 'openid profile email';
     639          $dataobject->email = 'some.email@some.de';
     640          $dataobject->username = 'someusername';
     641  
     642          $DB->insert_record('oauth2_system_account', $dataobject);
     643          // When a system account is registered the file_type FILE_CONTROLLED_LINK is supported.
     644          $this->assertEquals(FILE_INTERNAL | FILE_CONTROLLED_LINK,
     645              $this->repo->supported_returntypes());
     646      }
     647  
     648      /**
     649       * The reference_file_selected() methode is called every time a FILE_CONTROLLED_LINK is chosen for upload.
     650       * Since the function is very long the private function are tested separately, and merely the abortion of the
     651       * function are tested.
     652       *
     653       */
     654      public function test_reference_file_selected_error() {
     655          $this->repo->disabled = true;
     656          $this->expectException(\repository_exception::class);
     657          $this->repo->reference_file_selected('', context_system::instance(), '', '', '');
     658  
     659          $this->repo->disabled = false;
     660          $this->expectException(\repository_exception::class);
     661          $this->expectExceptionMessage('Cannot connect as system user');
     662          $this->repo->reference_file_selected('', context_system::instance(), '', '', '');
     663  
     664          $mock = $this->createMock(\core\oauth2\client::class);
     665          $mock->expects($this->once())->method('get_system_oauth_client')->with($this->issuer)->willReturn(true);
     666  
     667          $this->expectException(\repository_exception::class);
     668          $this->expectExceptionMessage('Cannot connect as current user');
     669          $this->repo->reference_file_selected('', context_system::instance(), '', '', '');
     670  
     671          $this->repo->expects($this->once())->method('get_user_oauth_client')->willReturn(true);
     672          $this->expectException(\repository_exception::class);
     673          $this->expectExceptionMessage('cannotdownload');
     674          $this->repo->reference_file_selected('', context_system::instance(), '', '', '');
     675  
     676          $this->repo->expects($this->once())->method('get_user_oauth_client')->willReturn(true);
     677          $this->expectException(\repository_exception::class);
     678          $this->expectExceptionMessage('cannotdownload');
     679          $this->repo->reference_file_selected('', context_system::instance(), '', '', '');
     680  
     681          $this->repo->expects($this->once())->method('get_user_oauth_client')->willReturn(true);
     682          $this->repo->expects($this->once())->method('copy_file_to_path')->willReturn(array('statuscode' =>
     683              array('success' => 400)));
     684          $this->expectException(\repository_exception::class);
     685          $this->expectExceptionMessage('Could not copy file');
     686          $this->repo->reference_file_selected('', context_system::instance(), '', '', '');
     687  
     688          $this->repo->expects($this->once())->method('get_user_oauth_client')->willReturn(true);
     689          $this->repo->expects($this->once())->method('copy_file_to_path')->willReturn(array('statuscode' =>
     690              array('success' => 201)));
     691          $this->repo->expects($this->once())->method('delete_share_dataowner_sysaccount')->willReturn(
     692              array('statuscode' => array('success' => 400)));
     693          $this->expectException(\repository_exception::class);
     694          $this->expectExceptionMessage('Share is still present');
     695          $this->repo->reference_file_selected('', context_system::instance(), '', '', '');
     696  
     697          $this->repo->expects($this->once())->method('get_user_oauth_client')->willReturn(true);
     698          $this->repo->expects($this->once())->method('copy_file_to_path')->willReturn(array('statuscode' =>
     699              array('success' => 201)));
     700          $this->repo->expects($this->once())->method('delete_share_dataowner_sysaccount')->willReturn(
     701              array('statuscode' => array('success' => 100)));
     702          $filereturn = array();
     703          $filereturn->link = 'some/fullpath' . 'some/target/path';
     704          $filereturn->name = 'mysource';
     705          $filereturn->usesystem = true;
     706          $filereturn = json_encode($filereturn);
     707          $return = $this->repo->reference_file_selected('mysource', context_system::instance(), '', '', '');
     708          $this->assertEquals($filereturn, $return);
     709      }
     710  
     711      /**
     712       * Test the send_file function for access controlled links.
     713       */
     714      public function test_send_file_errors() {
     715          $fs = get_file_storage();
     716          $storedfile = $fs->create_file_from_reference([
     717              'contextid' => context_system::instance()->id,
     718              'component' => 'core',
     719              'filearea'  => 'unittest',
     720              'itemid'    => 0,
     721              'filepath'  => '/',
     722              'filename'  => 'testfile.txt',
     723          ], $this->repo->id, json_encode([
     724              'type' => 'FILE_CONTROLLED_LINK',
     725              'link' => 'https://test.local/fakelink/',
     726              'usesystem' => true,
     727          ]));
     728          $this->set_private_property('', 'client');
     729          $this->expectException(repository_nextcloud\request_exception::class);
     730          $this->expectExceptionMessage(get_string('contactadminwith', 'repository_nextcloud',
     731              'The OAuth clients could not be connected.'));
     732  
     733          $this->repo->send_file($storedfile, '', '', '');
     734  
     735          // Testing whether the mock up appears is topic to behat.
     736          $mock = $this->createMock(\core\oauth2\client::class);
     737          $mock->expects($this->once())->method('is_logged_in')->willReturn(true);
     738          $this->repo->send_file($storedfile, '', '', '');
     739  
     740          // Checks that setting for foldername are used.
     741          $mock->expects($this->once())->method('is_dir')->with('Moodlefiles')->willReturn(false);
     742          // In case of false as return value mkcol is called to create the folder.
     743          $parsedwebdavurl = parse_url($this->issuer->get_endpoint_url('webdav'));
     744          $webdavprefix = $parsedwebdavurl['path'];
     745          $mock->expects($this->once())->method('mkcol')->with(
     746              $webdavprefix . 'Moodlefiles')->willReturn(400);
     747          $this->expectException(\repository_nextcloud\request_exception::class);
     748          $this->expectExceptionMessage(get_string('requestnotexecuted', 'repository_nextcloud'));
     749          $this->repo->send_file($storedfile, '', '', '');
     750  
     751          $expectedresponse = <<<XML
     752  <?xml version="1.0"?>
     753  <ocs>
     754   <meta>
     755    <status>ok</status>
     756    <statuscode>100</statuscode>
     757    <message/>
     758   </meta>
     759   <data>
     760    <element>
     761     <id>6</id>
     762     <share_type>0</share_type>
     763     <uid_owner>tech</uid_owner>
     764     <displayname_owner>tech</displayname_owner>
     765     <permissions>19</permissions>
     766     <stime>1511877999</stime>
     767     <parent/>
     768     <expiration/>
     769     <token/>
     770     <uid_file_owner>tech</uid_file_owner>
     771     <displayname_file_owner>tech</displayname_file_owner>
     772     <path>/System/Category Miscellaneous/Course Example Course/File morefiles/mod_resource/content/0/merge.txt</path>
     773     <item_type>file</item_type>
     774     <mimetype>text/plain</mimetype>
     775     <storage_id>home::tech</storage_id>
     776     <storage>4</storage>
     777     <item_source>824</item_source>
     778     <file_source>824</file_source>
     779     <file_parent>823</file_parent>
     780     <file_target>/merge (3).txt</file_target>
     781     <share_with>user2</share_with>
     782     <share_with_displayname>user1</share_with_displayname>
     783     <mail_send>0</mail_send>
     784    </element>
     785    <element>
     786     <id>5</id>
     787     <share_type>0</share_type>
     788     <uid_owner>tech</uid_owner>
     789     <displayname_owner>tech</displayname_owner>
     790     <permissions>19</permissions>
     791     <stime>1511877999</stime>
     792     <parent/>
     793     <expiration/>
     794     <token/>
     795     <uid_file_owner>tech</uid_file_owner>
     796     <displayname_file_owner>tech</displayname_file_owner>
     797     <path>/System/Category Miscellaneous/Course Example Course/File morefiles/mod_resource/content/0/merge.txt</path>
     798     <item_type>file</item_type>
     799     <mimetype>text/plain</mimetype>
     800     <storage_id>home::tech</storage_id>
     801     <storage>4</storage>
     802     <item_source>824</item_source>
     803     <file_source>824</file_source>
     804     <file_parent>823</file_parent>
     805     <file_target>/merged (3).txt</file_target>
     806     <share_with>user1</share_with>
     807     <share_with_displayname>user1</share_with_displayname>
     808     <mail_send>0</mail_send>
     809    </element>
     810   </data>
     811  </ocs>
     812  XML;
     813  
     814          // Checks that setting for foldername are used.
     815          $mock->expects($this->once())->method('is_dir')->with('Moodlefiles')->willReturn(true);
     816          // In case of true as return value mkcol is not called  to create the folder.
     817          $shareid = 5;
     818  
     819          $mockocsclient = $this->getMockBuilder(
     820              \repository_nextcloud\ocs_client::class)->disableOriginalConstructor()->disableOriginalClone()->getMock();
     821          $mockocsclient->expects($this->exactly(2))->method('call')->with('get_information_of_share',
     822              array('share_id' => $shareid))->will($this->returnValue($expectedresponse));
     823          $this->set_private_property($mock, 'ocsclient');
     824          $this->repo->expects($this->once())->method('move_file_to_folder')->with('/merged (3).txt', 'Moodlefiles',
     825              $mock)->willReturn(array('success' => 201));
     826  
     827          $this->repo->send_file('', '', '', '');
     828  
     829          // Create test for statuscode 403.
     830  
     831          // Checks that setting for foldername are used.
     832          $mock->expects($this->once())->method('is_dir')->with('Moodlefiles')->willReturn(true);
     833          // In case of true as return value mkcol is not called to create the folder.
     834          $shareid = 5;
     835          $mockocsclient = $this->getMockBuilder(\repository_nextcloud\ocs_client::class
     836          )->disableOriginalConstructor()->disableOriginalClone()->getMock();
     837          $mockocsclient->expects($this->exactly(1))->method('call')->with('get_shares',
     838              array('path' => '/merged (3).txt', 'reshares' => true))->will($this->returnValue($expectedresponse));
     839          $mockocsclient->expects($this->exactly(1))->method('call')->with('get_information_of_share',
     840              array('share_id' => $shareid))->will($this->returnValue($expectedresponse));
     841          $this->set_private_property($mock, 'ocsclient');
     842          $this->repo->expects($this->once())->method('move_file_to_folder')->with('/merged (3).txt', 'Moodlefiles',
     843              $mock)->willReturn(array('success' => 201));
     844          $this->repo->send_file('', '', '', '');
     845      }
     846  
     847      /**
     848       * Helper method, which inserts a given mock value into the repository_nextcloud object.
     849       *
     850       * @param mixed $value mock value that will be inserted.
     851       * @param string $propertyname name of the private property.
     852       * @return ReflectionProperty the resulting reflection property.
     853       */
     854      protected function set_private_property($value, $propertyname) {
     855          $refclient = new ReflectionClass($this->repo);
     856          $private = $refclient->getProperty($propertyname);
     857          $private->setAccessible(true);
     858          $private->setValue($this->repo, $value);
     859  
     860          return $private;
     861      }
     862      /**
     863       * Helper method to set required return parameters for get_listing.
     864       *
     865       * @return array array, which contains the parameters.
     866       */
     867      protected function get_initialised_return_array() {
     868          $ret = array();
     869          $ret['dynload'] = true;
     870          $ret['nosearch'] = true;
     871          $ret['nologin'] = false;
     872          $ret['path'] = [
     873              [
     874                  'name' => $this->repo->get_meta()->name,
     875                  'path' => '',
     876              ]
     877          ];
     878          $ret['manage'] = '';
     879          $ret['defaultreturntype'] = FILE_INTERNAL;
     880          $ret['list'] = array();
     881  
     882          return $ret;
     883      }
     884  }