Proyectos de Subversion Moodle

Rev

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