Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | Ultima modificación | Ver Log |

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