Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   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() {
  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  }