Proyectos de Subversion Moodle

Rev

| 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
namespace communication_matrix;
18
 
19
use core\context;
20
use core_communication\api;
21
use core_communication\communication_test_helper_trait;
22
use core_communication\processor;
23
use stored_file;
24
 
25
defined('MOODLE_INTERNAL') || die();
26
 
27
require_once(__DIR__ . '/matrix_test_helper_trait.php');
28
require_once(__DIR__ . '/../../../tests/communication_test_helper_trait.php');
29
 
30
/**
31
 * Class communication_feature_test to test the matrix features implemented using the core interfaces.
32
 *
33
 * @package    communication_matrix
34
 * @category   test
35
 * @copyright  2023 Safat Shahin <safat.shahin@moodle.com>
36
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 * @covers \communication_matrix\communication_feature
38
 * @coversDefaultClass \communication_matrix\communication_feature
39
 */
40
class communication_feature_test extends \advanced_testcase {
41
    use matrix_test_helper_trait;
42
    use communication_test_helper_trait;
43
 
44
    public function setUp(): void {
45
        parent::setUp();
46
        $this->resetAfterTest();
47
        $this->setup_communication_configs();
48
        $this->initialise_mock_server();
49
    }
50
 
51
    /**
52
     * Test create or update chat room.
53
     *
54
     * @covers ::create_chat_room
55
     */
56
    public function test_create_chat_room(): void {
57
        // Set up the test data first.
58
        $communication = \core_communication\api::load_by_instance(
59
            context: \core\context\system::instance(),
60
            component: 'communication_matrix',
61
            instancetype: 'example',
62
            instanceid: 1,
63
            provider: 'communication_matrix',
64
        );
65
 
66
        $communication->create_and_configure_room(
67
            communicationroomname: 'Room name',
68
            instance: (object) [
69
                'matrixroomtopic' => 'A fun topic',
70
            ],
71
        );
72
 
73
        // phpcs:ignore moodle.Commenting.InlineComment.DocBlock
74
        /** @var communication_feature */
75
        $provider = $communication->get_room_provider();
76
        $this->assertInstanceOf(
77
            communication_feature::class,
78
            $provider,
79
        );
80
 
81
        // Run the create_chat_room task.
82
        $result = $provider->create_chat_room();
83
        $this->assertTrue($result);
84
 
85
        // Ensure that a room_id was set.
86
        $this->assertNotEmpty($provider->get_room_id());
87
 
88
        // Fetch the back office room data.
89
        $remoteroom = $this->backoffice_get_room();
90
 
91
        // The roomid set in the database must match the one set on the remote server.
92
        $this->assertEquals(
93
            $remoteroom->room_id,
94
            $provider->get_room_id(),
95
        );
96
 
97
        // The name is a feature of the communication API itself.
98
        $this->assertEquals(
99
            'Room name',
100
            $communication->get_room_name(),
101
        );
102
        $this->assertEquals(
103
            $communication->get_room_name(),
104
            $remoteroom->name,
105
        );
106
 
107
        // The topic is a Matrix feature.
108
        $roomconfig = $provider->get_room_configuration();
109
        $this->assertEquals(
110
            'A fun topic',
111
            $roomconfig->get_topic(),
112
        );
113
        $this->assertEquals(
114
            $remoteroom->topic,
115
            $roomconfig->get_topic(),
116
        );
117
 
118
        // The avatar features are checked in a separate test.
119
    }
120
 
121
    /**
122
     * Test update of a chat room.
123
     *
124
     * @covers ::update_chat_room
125
     */
126
    public function test_update_chat_room(): void {
127
        $communication = $this->create_room(
128
            roomname: 'Our room name',
129
            roomtopic: 'Our room topic',
130
        );
131
 
132
        // phpcs:ignore moodle.Commenting.InlineComment.DocBlock
133
        /** @var communication_feature */
134
        $provider = $communication->get_room_provider();
135
        $this->assertInstanceOf(
136
            communication_feature::class,
137
            $provider,
138
        );
139
 
140
        // Update the room name.
141
        // Note: We have to update the record via the API, and then call the provider update method.
142
        // That's because the update is performed asynchronously.
143
        $communication->update_room(
144
            communicationroomname: 'Our updated room name',
145
        );
146
        $provider->reload();
147
 
148
        // Now call the provider's update method.
149
        $provider->update_chat_room();
150
 
151
        // And assert that it was updated remotely.
152
        $remoteroom = $this->backoffice_get_room();
153
 
154
        $this->assertEquals(
155
            'Our updated room name',
156
            $communication->get_room_name(),
157
        );
158
        $this->assertEquals(
159
            $communication->get_room_name(),
160
            $remoteroom->name,
161
        );
162
        // The remote topic should not have changed.
163
        $this->assertEquals(
164
            'Our room topic',
165
            $remoteroom->topic,
166
        );
167
 
168
        // Now update just the topic.
169
        // First in the local API.
170
        $communication->update_room(
171
            instance: (object) [
172
                'matrixroomtopic' => 'Our updated room topic',
173
            ],
174
        );
175
 
176
        $provider->reload();
177
 
178
        // Then call the provider's update method to actually perform the change.
179
        $provider->update_chat_room();
180
 
181
        // And assert that it was updated remotely.
182
        $remoteroom = $this->backoffice_get_room();
183
 
184
        $this->assertEquals(
185
            'Our updated room topic',
186
            $provider->get_room_configuration()->get_topic(),
187
        );
188
 
189
        // The remote topic should have been updated.
190
        $this->assertEquals(
191
            'Our updated room topic',
192
            $remoteroom->topic,
193
        );
194
 
195
        // The name should not have changed.
196
        $this->assertEquals(
197
            'Our updated room name',
198
            $communication->get_room_name(),
199
        );
200
    }
201
 
202
    /**
203
     * Test delete chat room.
204
     *
205
     * @covers ::delete_chat_room
206
     */
207
    public function test_delete_chat_room(): void {
208
        $communication = $this->create_room();
209
 
210
        $processor = $communication->get_processor();
211
        $provider = $communication->get_room_provider();
212
        $room = matrix_room::load_by_processor_id($processor->get_id());
213
 
214
        // Run the delete method.
215
        $this->assertTrue($provider->delete_chat_room());
216
 
217
        // The record of the room should have been removed.
218
        $this->assertNull(matrix_room::load_by_processor_id($processor->get_id()));
219
 
220
        // But the room itself shoudl exist.
221
        $matrixroomdata = $this->get_matrix_room_data($room->get_room_id());
222
 
223
        $this->assertNotEmpty($matrixroomdata);
224
        $this->assertEquals($processor->get_room_name(), $matrixroomdata->name);
225
        $this->assertEquals($room->get_topic(), $matrixroomdata->topic);
226
    }
227
 
228
    /**
229
     * Test update room avatar.
230
     *
231
     * @covers ::update_room_avatar
232
     * @dataProvider avatar_provider
233
     */
234
    public function test_update_room_avatar(
235
        ?string $before,
236
        ?string $after,
237
    ): void {
238
        $this->setAdminUser();
239
 
240
        // Create a new draft file.
241
        $logo = $this->create_communication_file('moodle_logo.jpg', 'logo.jpg');
242
        $circle = $this->create_communication_file('circle.png', 'circle.png');
243
 
244
        if ($before === 'logo') {
245
            $before = $logo;
246
        } else if ($before === 'circle') {
247
            $before = $circle;
248
        }
249
 
250
        if ($after === 'logo') {
251
            $after = $logo;
252
        } else if ($after === 'circle') {
253
            $after = $circle;
254
        }
255
 
256
        $communication = $this->create_matrix_room(
257
            component: 'communication_matrix',
258
            itemtype: 'example_room',
259
            itemid: 1,
260
            roomname: 'Example room name',
261
            roomavatar: $before,
262
        );
263
 
264
        // Confirm that the avatar was set remotely.
265
        $remoteroom = $this->backoffice_get_room();
266
 
267
        if ($before) {
268
            $this->assertStringEndsWith($before->get_filename(), $remoteroom->avatar);
269
            $avatarcontent = download_file_content($remoteroom->avatar);
270
            $this->assertEquals($before->get_content(), $avatarcontent);
271
        } else {
272
            $this->assertEmpty($remoteroom->avatar);
273
        }
274
 
275
        // Reload the API instance as the information stored has changed.
276
        $communication->reload();
277
 
278
        // Update the avatar with the 'after' avatar.
279
        $communication->update_room(
280
            avatar: $after,
281
        );
282
        $this->run_all_adhoc_tasks();
283
 
284
        // Confirm that the avatar was updated remotely.
285
        $remoteroom = $this->backoffice_get_room();
286
 
287
        if ($after) {
288
            $this->assertStringEndsWith($after->get_filename(), $remoteroom->avatar);
289
            $avatarcontent = download_file_content($remoteroom->avatar);
290
            $this->assertEquals($after->get_content(), $avatarcontent);
291
        } else {
292
            $this->assertEmpty($remoteroom->avatar);
293
        }
294
    }
295
 
296
    /**
297
     * Tests for setting and updating the room avatar.
298
     *
299
     * @return array
300
     */
301
    public static function avatar_provider(): array {
302
        return [
303
            'Empty to avatar' => [
304
                null,
305
                'circle',
306
            ],
307
            'Avatar to empty' => [
308
                'circle',
309
                null,
310
            ],
311
            'Avatar to new avatar' => [
312
                'circle',
313
                'logo',
314
            ],
315
        ];
316
    }
317
 
318
    /**
319
     * Test get chat room url.
320
     *
321
     * @covers ::get_chat_room_url
322
     */
323
    public function test_get_chat_room_url(): void {
324
        $communication = $this->create_room();
325
 
326
        $provider = $communication->get_room_provider();
327
 
328
        $url = $provider->get_chat_room_url();
329
        $this->assertNotNull($url);
330
 
331
        // Fetch the room information from the server.
332
        $remoteroom = $this->backoffice_get_room();
333
 
334
        $this->assertStringEndsWith(
335
            $remoteroom->room_id,
336
            $url,
337
        );
338
    }
339
 
340
    /**
341
     * Test create members.
342
     *
343
     * @covers ::create_members
344
     * @covers ::add_registered_matrix_user_to_room
345
     */
346
    public function test_create_members(): void {
347
        $user = $this->getDataGenerator()->create_user();
348
 
349
        $communication = $this->create_room(
350
            members: [
351
                $user->id,
352
            ],
353
        );
354
 
355
        $remoteroom = $this->backoffice_get_room();
356
        $this->assertCount(1, $remoteroom->members);
357
        $member = reset($remoteroom->members);
358
        $this->assertStringStartsWith("@{$user->username}", $member->userid);
359
    }
360
 
361
    /**
362
     * Test add/remove members from room.
363
     *
364
     * @covers ::remove_members_from_room
365
     * @covers ::add_members_to_room
366
     * @covers ::add_registered_matrix_user_to_room
367
     * @covers ::check_room_membership
368
     * @covers ::set_matrix_power_levels
369
     */
370
    public function test_add_and_remove_members_from_room(): void {
371
        $user = $this->getDataGenerator()->create_user();
372
        $user2 = $this->getDataGenerator()->create_user();
373
 
374
        $communication = $this->create_room();
375
        $provider = $communication->get_room_user_provider();
376
 
377
        $remoteroom = $this->backoffice_get_room();
378
        $this->assertCount(0, $remoteroom->members);
379
 
380
        // Add the members to the room.
381
        $provider->add_members_to_room([$user->id, $user2->id]);
382
 
383
        // Ensure that they have been created.
384
        $remoteroom = $this->backoffice_get_room();
385
        $this->assertCount(2, $remoteroom->members);
386
 
387
        $userids = array_map(fn($member) => $member->userid, $remoteroom->members);
388
        $userids = array_map(fn($userid) => substr($userid, 0, strpos($userid, ':')), $userids);
389
        $this->assertContains("@{$user->username}", $userids);
390
        $this->assertContains("@{$user2->username}", $userids);
391
 
392
        // Remove member from matrix room.
393
        $provider->remove_members_from_room([$user->id]);
394
 
395
        // Ensure that they have been removed.
396
        $remoteroom = $this->backoffice_get_room();
397
        $members = (array) $remoteroom->members;
398
        $this->assertCount(1, $members);
399
        $userids = array_map(fn ($member) => $member->userid, $members);
400
        $userids = array_map(fn ($userid) => substr($userid, 0, strpos($userid, ':')), $userids);
401
        $this->assertNotContains("@{$user->username}", $userids);
402
        $this->assertContains("@{$user2->username}", $userids);
403
    }
404
 
405
    /**
406
     * Test update of room membership.
407
     *
408
     * @covers ::update_room_membership
409
     * @covers ::set_matrix_power_levels
410
     * @covers ::is_power_levels_update_required
411
     * @covers ::get_user_allowed_power_level
412
     */
413
    public function test_update_room_membership(): void {
414
        $this->resetAfterTest();
415
 
416
        global $DB;
417
 
418
        // Create a new room.
419
        $course = $this->get_course('Sampleroom', 'none');
420
        $coursecontext = \context_course::instance($course->id);
421
        $user = $this->get_user();
422
 
423
        $communication = $this->create_room(
424
            component: 'core_course',
425
            itemtype: 'coursecommunication',
426
            itemid: $course->id,
427
            roomname: 'sampleroom',
428
            roomtopic: 'sampltopic',
429
            roomavatar: null,
430
            members: [$user->id],
431
            context: $coursecontext,
432
        );
433
 
434
        $provider = $communication->get_room_user_provider();
435
 
436
        // Add the members to the room.
437
        $provider->add_members_to_room([$user->id]);
438
 
439
        // Assign teacher role to the user.
440
        $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
441
        $this->getDataGenerator()->enrol_user($user->id, $course->id);
442
        role_assign($teacherrole->id, $user->id, $coursecontext->id);
443
 
444
        // Test the tasks added as the role is a teacher.
445
        $provider->update_room_membership([$user->id]);
446
 
447
        $processor = \core_communication\processor::load_by_instance(
448
            context: $coursecontext,
449
            component: 'core_course',
450
            instancetype: 'coursecommunication',
451
            instanceid: $course->id,
452
        );
453
        $synceduser = $processor->get_instance_userids(
454
            synced: true,
455
        );
456
        $synceduser = reset($synceduser);
457
 
458
        // Test if the communication user record is synced.
459
        $this->assertEquals($user->id, $synceduser);
460
    }
461
 
462
    /**
463
     * Test the user power level allocation according to context.
464
     *
465
     * @covers ::get_user_allowed_power_level
466
     */
467
    public function test_get_user_allowed_power_level(): void {
468
        $this->resetAfterTest();
469
        global $DB;
470
 
471
        // Create users.
472
        $user1 = $this->getDataGenerator()->create_user();
473
        $user2 = $this->getDataGenerator()->create_user();
474
 
475
        $course = $this->get_course();
476
        $coursecontext = \context_course::instance($course->id);
477
        $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']);
478
        $studentrole = $DB->get_record('role', ['shortname' => 'student']);
479
        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
480
        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
481
        // Assign roles.
482
        role_assign($teacherrole->id, $user1->id, $coursecontext->id);
483
        role_assign($studentrole->id, $user2->id, $coursecontext->id);
484
 
485
        $communicationprocessor = processor::load_by_instance(
486
            context: \core\context\course::instance($course->id),
487
            component: 'core_course',
488
            instancetype: 'coursecommunication',
489
            instanceid: $course->id
490
        );
491
 
492
        // Test if the power level is set according to the context.
493
        $this->assertEquals(
494
            matrix_constants::POWER_LEVEL_MOODLE_MODERATOR,
495
            $communicationprocessor->get_room_provider()->get_user_allowed_power_level($user1->id)
496
        );
497
        $this->assertEquals(
498
            matrix_constants::POWER_LEVEL_DEFAULT,
499
            $communicationprocessor->get_room_provider()->get_user_allowed_power_level($user2->id)
500
        );
501
    }
502
 
503
    /**
504
     * Helper to create a room.
505
     *
506
     * @param null|string $component
507
     * @param null|string $itemtype
508
     * @param null|int $itemid
509
     * @param null|string $roomname
510
     * @param null|string $roomtopic
511
     * @param null|stored_file $roomavatar
512
     * @param array $members
513
     * @return api
514
     */
515
    protected function create_room(
516
        ?string $component = 'communication_matrix',
517
        ?string $itemtype = 'example',
518
        ?int $itemid = 1,
519
        ?string $roomname = null,
520
        ?string $roomtopic = null,
521
        ?\stored_file $roomavatar = null,
522
        array $members = [],
523
        ?context $context = null,
524
    ): \core_communication\api {
525
        // Create a new room.
526
        $communication = \core_communication\api::load_by_instance(
527
            context: $context ?? \core\context\system::instance(),
528
            component: $component,
529
            instancetype: $itemtype,
530
            instanceid: $itemid,
531
            provider: 'communication_matrix',
532
        );
533
 
534
        $communication->create_and_configure_room(
535
            communicationroomname: $roomname ?? 'Room name',
536
            avatar: $roomavatar,
537
            instance: (object) [
538
                'matrixroomtopic' => $roomtopic ?? 'A fun topic',
539
            ],
540
        );
541
 
542
        $communication->add_members_to_room($members);
543
 
544
        // Run the adhoc task.
545
        $this->run_all_adhoc_tasks();
546
 
547
        $communication->reload();
548
        return $communication;
549
    }
550
 
551
    /**
552
     * Test if the selected provider is configured.
553
     *
554
     * @covers ::is_configured
555
     */
556
    public function test_is_configured(): void {
557
        $course = $this->get_course();
558
        $communicationprocessor = processor::load_by_instance(
559
            context: \core\context\course::instance($course->id),
560
            component: 'core_course',
561
            instancetype: 'coursecommunication',
562
            instanceid: $course->id
563
        );
564
        $this->assertTrue($communicationprocessor->get_room_provider()->is_configured());
565
 
566
        // Unset communication_matrix settings.
567
        unset_config('matrixhomeserverurl', 'communication_matrix');
568
        unset_config('matrixaccesstoken', 'communication_matrix');
569
        $this->assertFalse($communicationprocessor->get_room_provider()->is_configured());
570
    }
571
}