Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   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 GuzzleHttp\Psr7\Response;
  21  
  22  /**
  23   * Trait matrix_helper_trait to generate initial setup for matrix mock and associated helpers.
  24   *
  25   * @package    communication_matrix
  26   * @copyright  2023 Safat Shahin <safat.shahin@moodle.com>
  27   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  trait matrix_test_helper_trait {
  30      /**
  31       * @var string $accesstoken The token for matrix connection
  32       */
  33      protected string $accesstoken;
  34  
  35      /**
  36       * @var string $matrixhomeserverurl The server url of matrix synapse server
  37       */
  38      protected string $matrixhomeserverurl;
  39  
  40      /**
  41       * Initialize the mock configs in settings.
  42       *
  43       * @return void
  44       */
  45      protected function initialise_mock_configs(): void {
  46          $this->matrixhomeserverurl = TEST_COMMUNICATION_MATRIX_MOCK_SERVER;
  47          set_config('matrixhomeserverurl', $this->matrixhomeserverurl, 'communication_matrix');
  48          $request = $this->request();
  49          $response = $request->post($this->matrixhomeserverurl . '/backoffice/create-admin');
  50          $admindata = json_decode($response->getBody());
  51          $json = [
  52              'identifier' => [
  53                  'type' => 'm.id.user',
  54                  'user' => $admindata->user_id,
  55              ],
  56              'type' => 'm.login.password',
  57              'password' => $admindata->password,
  58          ];
  59          $request = $this->request($json);
  60          $response = $request->post($this->matrixhomeserverurl . '/_matrix/client/r0/login');
  61          $response = json_decode($response->getBody());
  62          if (empty($response->access_token)) {
  63              $this->markTestSkipped(
  64                  'The matrix mock server is not responsive, can not continue the tests'
  65              );
  66          }
  67          $this->accesstoken = $response->access_token;
  68          set_config('matrixaccesstoken', $this->accesstoken, 'communication_matrix');
  69      }
  70  
  71      /**
  72       * Get the mock server url.
  73       *
  74       * @return string
  75       */
  76      public function get_matrix_server_url(): string {
  77          if (empty($this->matrixhomeserverurl)) {
  78              throw new \coding_exception('Can not get this information without initializing the mock server.');
  79          }
  80          return $this->matrixhomeserverurl;
  81      }
  82  
  83      /**
  84       * Get the matrix access token.
  85       *
  86       * @return string
  87       */
  88      public function get_matrix_access_token(): string {
  89          if (empty($this->accesstoken)) {
  90              throw new \coding_exception('Can not get this information without initializing the mock server.');
  91          }
  92          return $this->accesstoken;
  93      }
  94  
  95      /**
  96       * This test requires mock server to be present.
  97       *
  98       * @return void
  99       */
 100      protected function initialise_mock_server(): void {
 101          if (!defined('TEST_COMMUNICATION_MATRIX_MOCK_SERVER')) {
 102              $this->markTestSkipped(
 103                  'The TEST_COMMUNICATION_MATRIX_MOCK_SERVER constant must be defined to run communication_matrix tests'
 104              );
 105          }
 106          $this->reset_mock();
 107          $this->initialise_mock_configs();
 108      }
 109  
 110      /**
 111       * Get matrix room data from matrix server.
 112       *
 113       * @param string $roomid The id of the room
 114       * @return \stdClass
 115       */
 116      public function get_matrix_room_data(string $roomid): \stdClass {
 117          $rooms = $this->backoffice_get_all_rooms();
 118          foreach ($rooms as $room) {
 119              if ($room->room_id === $roomid) {
 120                  return $room;
 121              }
 122          }
 123      }
 124  
 125      /**
 126       * Get matrix user data from matrix server.
 127       *
 128       * @param string $roomid The id of the room
 129       * @param string $matrixuserid The id of the user
 130       * @return \stdClass
 131       */
 132      public function get_matrix_user_data(string $roomid, string $matrixuserid): \stdClass {
 133          $users = $this->backoffice_get_all_users();
 134  
 135          foreach ($users as $user) {
 136              if ($user->userid === $matrixuserid) {
 137                  return $user;
 138              }
 139          }
 140      }
 141  
 142      /**
 143       * A backoffice call to get all registered users from our mock server.
 144       *
 145       * @return array
 146       */
 147      public function backoffice_get_all_users(): array {
 148          $client = new \core\http_client();
 149  
 150          return json_decode($client->get($this->get_backoffice_uri('users'))->getBody())->users;
 151      }
 152  
 153      /**
 154       * A backoffice method to create users and rooms on our mock server.
 155       *
 156       * @param array $users
 157       * @param array $rooms
 158       */
 159      public function backoffice_create_users_and_rooms(
 160          array $users = [],
 161          array $rooms = [],
 162      ): Response {
 163          $client = new \core\http_client();
 164          return $client->put(
 165              $this->get_backoffice_uri('create'),
 166              [
 167                  'json' => [
 168                      'users' => $users,
 169                      'rooms' => $rooms,
 170                  ],
 171              ],
 172          );
 173      }
 174  
 175      /**
 176       * The http request for the api call.
 177       *
 178       * @param array $jsonarray The array of json
 179       * @param array $headers The array of headers
 180       * @return \core\http_client
 181       */
 182      public function request(array $jsonarray = [], array $headers = []): \core\http_client {
 183          $response = new \core\http_client([
 184              'headers' => $headers,
 185              'json' => $jsonarray,
 186          ]);
 187          return $response;
 188      }
 189  
 190      /**
 191       * Get the URI of a backoffice endpoint on the mock server.
 192       *
 193       * @param string $endpoint
 194       * @return string
 195       */
 196      protected function get_backoffice_uri(string $endpoint): string {
 197          return $this->get_matrix_server_url() . '/backoffice/' . $endpoint;
 198      }
 199  
 200      /**
 201       * Fetch all rooms from the back office.
 202       *
 203       * @return array
 204       */
 205      public function backoffice_get_all_rooms(): array {
 206          $client = new \core\http_client();
 207  
 208          return json_decode($client->get($this->get_backoffice_uri('rooms'))->getBody())->rooms;
 209      }
 210  
 211      /**
 212       * Return the first room from the server.
 213       *
 214       * In most cases there is only one room.
 215       * @return \stdClass
 216       */
 217      public function backoffice_get_room(): \stdClass {
 218          // Fetch the room information from the server.
 219          $rooms = $this->backoffice_get_all_rooms();
 220          $this->assertCount(1, $rooms);
 221          $room = reset($rooms);
 222          return $room;
 223      }
 224  
 225      /**
 226       * Reset the mock server
 227       *
 228       * @return void
 229       */
 230      public function reset_mock(): void {
 231          if (defined('TEST_COMMUNICATION_MATRIX_MOCK_SERVER')) {
 232              $request = $this->request();
 233              $response = $request->post(TEST_COMMUNICATION_MATRIX_MOCK_SERVER . '/backoffice/reset');
 234              $response = json_decode($response->getBody());
 235              if (empty($response->reset)) {
 236                  $this->markTestSkipped(
 237                      'The matrix mock server is not responsive, can not continue the tests'
 238                  );
 239              }
 240          }
 241      }
 242  
 243      /**
 244       * Helper to create a room.
 245       *
 246       * @param null|string $component
 247       * @param null|string $itemtype
 248       * @param null|int $itemid
 249       * @param null|string $roomname
 250       * @param null|string $roomtopic
 251       * @param null|stored_file $roomavatar
 252       * @param array $members
 253       * @return api
 254       */
 255      protected function create_matrix_room(
 256          ?string $component = 'core_course',
 257          ?string $itemtype = 'example',
 258          ?int $itemid = 1,
 259          ?string $roomname = null,
 260          ?string $roomtopic = null,
 261          ?\stored_file $roomavatar = null,
 262          array $members = [],
 263          ?context $context = null,
 264      ): \core_communication\api {
 265          $context = $context ?? \core\context\system::instance();
 266          // Create a new room.
 267          $communication = \core_communication\api::load_by_instance(
 268              context: $context,
 269              component: $component,
 270              instancetype: $itemtype,
 271              instanceid: $itemid,
 272              provider: 'communication_matrix',
 273          );
 274  
 275          $communication->create_and_configure_room(
 276              communicationroomname: $roomname ?? 'Room name',
 277              avatar: $roomavatar,
 278              instance: (object) [
 279                  'matrixroomtopic' => $roomtopic ?? 'A fun topic',
 280              ],
 281          );
 282  
 283          $communication->add_members_to_room($members);
 284  
 285          // Run the adhoc task.
 286          $this->run_all_adhoc_tasks();
 287  
 288          return \core_communication\api::load_by_instance(
 289              context: $context,
 290              component: $component,
 291              instancetype: $itemtype,
 292              instanceid: $itemid,
 293          );
 294      }
 295  }