Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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 core_message;
  18  
  19  use core_message\tests\helper as testhelper;
  20  use core_message_external;
  21  use externallib_advanced_testcase;
  22  
  23  defined('MOODLE_INTERNAL') || die();
  24  
  25  global $CFG;
  26  
  27  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  28  require_once($CFG->dirroot . '/message/externallib.php');
  29  
  30  /**
  31   * External message functions unit tests
  32   *
  33   * @package    core_message
  34   * @category   external
  35   * @copyright  2012 Jerome Mouneyrac
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class externallib_test extends externallib_advanced_testcase {
  39  
  40      /**
  41       * Tests set up
  42       */
  43      protected function setUp(): void {
  44          global $CFG;
  45  
  46          require_once($CFG->dirroot . '/message/lib.php');
  47      }
  48  
  49      /**
  50       * Send a fake message.
  51       *
  52       * {@link message_send()} does not support transaction, this function will simulate a message
  53       * sent from a user to another. We should stop using it once {@link message_send()} will support
  54       * transactions. This is not clean at all, this is just used to add rows to the table.
  55       *
  56       * @param \stdClass $userfrom user object of the one sending the message.
  57       * @param \stdClass $userto user object of the one receiving the message.
  58       * @param string $message message to send.
  59       * @param int $notification is the message a notification.
  60       * @param int $time the time the message was sent
  61       */
  62      protected function send_message($userfrom, $userto, $message = 'Hello world!', $notification = 0, $time = 0) {
  63          global $DB;
  64  
  65          if (empty($time)) {
  66              $time = time();
  67          }
  68  
  69          if ($notification) {
  70              $record = new \stdClass();
  71              $record->useridfrom = $userfrom->id;
  72              $record->useridto = $userto->id;
  73              $record->subject = 'No subject';
  74              $record->fullmessage = $message;
  75              $record->smallmessage = $message;
  76              $record->timecreated = $time;
  77  
  78              return $DB->insert_record('notifications', $record);
  79          }
  80  
  81          if (!$conversationid = \core_message\api::get_conversation_between_users([$userfrom->id, $userto->id])) {
  82              $conversation = \core_message\api::create_conversation(
  83                  \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
  84                  [
  85                      $userfrom->id,
  86                      $userto->id
  87                  ]
  88              );
  89              $conversationid = $conversation->id;
  90          }
  91  
  92          // Ok, send the message.
  93          $record = new \stdClass();
  94          $record->useridfrom = $userfrom->id;
  95          $record->conversationid = $conversationid;
  96          $record->subject = 'No subject';
  97          $record->fullmessage = $message;
  98          $record->smallmessage = $message;
  99          $record->timecreated = $time;
 100  
 101          return $DB->insert_record('messages', $record);
 102      }
 103  
 104      /**
 105       * Test send_instant_messages.
 106       */
 107      public function test_send_instant_messages() {
 108          global $DB, $USER;
 109  
 110          $this->resetAfterTest();
 111  
 112          // Transactions used in tests, tell phpunit use alternative reset method.
 113          $this->preventResetByRollback();
 114  
 115          $user1 = self::getDataGenerator()->create_user();
 116          $user2 = self::getDataGenerator()->create_user();
 117  
 118          $this->setUser($user1);
 119  
 120          // Create test message data.
 121          $message1 = array();
 122          $message1['touserid'] = $user2->id;
 123          $message1['text'] = 'the message.';
 124          $message1['clientmsgid'] = 4;
 125          $messages = array($message1);
 126  
 127          $sentmessages = core_message_external::send_instant_messages($messages);
 128          $sentmessages = \external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 129          $this->assertEquals(
 130              get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message1['touserid']))),
 131              array_pop($sentmessages)['errormessage']
 132          );
 133  
 134          // Add the user1 as a contact.
 135          \core_message\api::add_contact($user1->id, $user2->id);
 136  
 137          // Send message again. Now it should work properly.
 138          $sentmessages = core_message_external::send_instant_messages($messages);
 139          // We need to execute the return values cleaning process to simulate the web service server.
 140          $sentmessages = \external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 141  
 142          $sentmessage = reset($sentmessages);
 143  
 144          $sql = "SELECT m.*, mcm.userid as useridto
 145                   FROM {messages} m
 146             INNER JOIN {message_conversations} mc
 147                     ON m.conversationid = mc.id
 148             INNER JOIN {message_conversation_members} mcm
 149                     ON mcm.conversationid = mc.id
 150                  WHERE mcm.userid != ?
 151                    AND m.id = ?";
 152          $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
 153  
 154          // Confirm that the message was inserted correctly.
 155          $this->assertEquals($themessage->useridfrom, $user1->id);
 156          $this->assertEquals($themessage->useridto, $message1['touserid']);
 157          $this->assertEquals($themessage->smallmessage, $message1['text']);
 158          $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
 159      }
 160  
 161      /**
 162       * Test send_instant_messages with a message text longer than permitted.
 163       */
 164      public function test_send_instant_messages_long_text() {
 165          global $CFG;
 166  
 167          $this->resetAfterTest(true);
 168  
 169          // Transactions used in tests, tell phpunit use alternative reset method.
 170          $this->preventResetByRollback();
 171  
 172          $user1 = self::getDataGenerator()->create_user();
 173          $user2 = self::getDataGenerator()->create_user();
 174  
 175          $this->setUser($user1);
 176  
 177          // Create test message data.
 178          $message1 = [
 179              'touserid' => $user2->id,
 180              'text' => str_repeat("M", \core_message\api::MESSAGE_MAX_LENGTH + 100),
 181              'clientmsgid' => 4,
 182          ];
 183          $messages = [$message1];
 184  
 185          // Add the user1 as a contact.
 186          \core_message\api::add_contact($user1->id, $user2->id);
 187  
 188          $sentmessages = core_message_external::send_instant_messages($messages);
 189          $sentmessages = \external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 190          $this->assertEquals(
 191              get_string('errormessagetoolong', 'message'),
 192              array_pop($sentmessages)['errormessage']
 193          );
 194      }
 195  
 196      /**
 197       * Test send_instant_messages to a user who has blocked you.
 198       */
 199      public function test_send_instant_messages_blocked_user() {
 200          global $DB;
 201  
 202          $this->resetAfterTest();
 203  
 204          // Transactions used in tests, tell phpunit use alternative reset method.
 205          $this->preventResetByRollback();
 206  
 207          $user1 = self::getDataGenerator()->create_user();
 208          $user2 = self::getDataGenerator()->create_user();
 209  
 210          $this->setUser($user1);
 211  
 212          \core_message\api::block_user($user2->id, $user1->id);
 213  
 214          // Create test message data.
 215          $message1 = array();
 216          $message1['touserid'] = $user2->id;
 217          $message1['text'] = 'the message.';
 218          $message1['clientmsgid'] = 4;
 219          $messages = array($message1);
 220  
 221          $sentmessages = core_message_external::send_instant_messages($messages);
 222          $sentmessages = \external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 223  
 224          $sentmessage = reset($sentmessages);
 225  
 226          $this->assertEquals(get_string('usercantbemessaged', 'message', fullname($user2)), $sentmessage['errormessage']);
 227  
 228          $this->assertEquals(0, $DB->count_records('messages'));
 229      }
 230  
 231      /**
 232       * Test send_instant_messages when sending a message to a non-contact who has blocked non-contacts.
 233       */
 234      public function test_send_instant_messages_block_non_contacts() {
 235          global $DB;
 236  
 237          $this->resetAfterTest(true);
 238  
 239          // Transactions used in tests, tell phpunit use alternative reset method.
 240          $this->preventResetByRollback();
 241  
 242          $user1 = self::getDataGenerator()->create_user();
 243          $user2 = self::getDataGenerator()->create_user();
 244  
 245          $this->setUser($user1);
 246  
 247          // Set the user preference so user 2 does not accept messages from non-contacts.
 248          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS, $user2);
 249  
 250          // Create test message data.
 251          $message1 = array();
 252          $message1['touserid'] = $user2->id;
 253          $message1['text'] = 'the message.';
 254          $message1['clientmsgid'] = 4;
 255          $messages = array($message1);
 256  
 257          $sentmessages = core_message_external::send_instant_messages($messages);
 258          $sentmessages = \external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 259  
 260          $sentmessage = reset($sentmessages);
 261  
 262          $this->assertEquals(get_string('usercantbemessaged', 'message', fullname($user2)), $sentmessage['errormessage']);
 263  
 264          $this->assertEquals(0, $DB->count_records('messages'));
 265      }
 266  
 267      /**
 268       * Test send_instant_messages when sending a message to a contact who has blocked non-contacts.
 269       */
 270      public function test_send_instant_messages_block_non_contacts_but_am_contact() {
 271          global $DB, $USER;
 272  
 273          $this->resetAfterTest(true);
 274  
 275          // Transactions used in tests, tell phpunit use alternative reset method.
 276          $this->preventResetByRollback();
 277  
 278          $user1 = self::getDataGenerator()->create_user();
 279          $user2 = self::getDataGenerator()->create_user();
 280  
 281          $this->setUser($user1);
 282  
 283          // Set the user preference so user 2 does not accept messages from non-contacts.
 284          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS, $user2);
 285  
 286          \core_message\api::add_contact($user1->id, $user2->id);
 287  
 288          // Create test message data.
 289          $message1 = array();
 290          $message1['touserid'] = $user2->id;
 291          $message1['text'] = 'the message.';
 292          $message1['clientmsgid'] = 4;
 293          $messages = array($message1);
 294  
 295          $sentmessages = core_message_external::send_instant_messages($messages);
 296          $sentmessages = \external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 297  
 298          $sentmessage = reset($sentmessages);
 299  
 300          $sql = "SELECT m.*, mcm.userid as useridto
 301                   FROM {messages} m
 302             INNER JOIN {message_conversations} mc
 303                     ON m.conversationid = mc.id
 304             INNER JOIN {message_conversation_members} mcm
 305                     ON mcm.conversationid = mc.id
 306                  WHERE mcm.userid != ?
 307                    AND m.id = ?";
 308          $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
 309  
 310          // Confirm that the message was inserted correctly.
 311          $this->assertEquals($themessage->useridfrom, $user1->id);
 312          $this->assertEquals($themessage->useridto, $message1['touserid']);
 313          $this->assertEquals($themessage->smallmessage, $message1['text']);
 314          $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
 315      }
 316  
 317      /**
 318       * Test send_instant_messages with no capabilities
 319       */
 320      public function test_send_instant_messages_no_capability() {
 321          global $DB;
 322  
 323          $this->resetAfterTest(true);
 324  
 325          // Transactions used in tests, tell phpunit use alternative reset method.
 326          $this->preventResetByRollback();
 327  
 328          $user1 = self::getDataGenerator()->create_user();
 329          $user2 = self::getDataGenerator()->create_user();
 330  
 331          $this->setUser($user1);
 332  
 333          // Unset the required capabilities by the external function.
 334          $contextid = \context_system::instance()->id;
 335          $userrole = $DB->get_record('role', array('shortname' => 'user'));
 336          $this->unassignUserCapability('moodle/site:sendmessage', $contextid, $userrole->id);
 337  
 338          // Create test message data.
 339          $message1 = array();
 340          $message1['touserid'] = $user2->id;
 341          $message1['text'] = 'the message.';
 342          $message1['clientmsgid'] = 4;
 343          $messages = array($message1);
 344  
 345          $this->expectException('required_capability_exception');
 346          core_message_external::send_instant_messages($messages);
 347      }
 348  
 349      /**
 350       * Test send_instant_messages when messaging is disabled.
 351       */
 352      public function test_send_instant_messages_messaging_disabled() {
 353          global $CFG;
 354  
 355          $this->resetAfterTest(true);
 356  
 357          // Transactions used in tests, tell phpunit use alternative reset method.
 358          $this->preventResetByRollback();
 359  
 360          $user1 = self::getDataGenerator()->create_user();
 361          $user2 = self::getDataGenerator()->create_user();
 362  
 363          $this->setUser($user1);
 364  
 365          // Disable messaging.
 366          $CFG->messaging = 0;
 367  
 368          // Create test message data.
 369          $message1 = array();
 370          $message1['touserid'] = $user2->id;
 371          $message1['text'] = 'the message.';
 372          $message1['clientmsgid'] = 4;
 373          $messages = array($message1);
 374  
 375          $this->expectException('moodle_exception');
 376          core_message_external::send_instant_messages($messages);
 377      }
 378  
 379      /**
 380       * Test delete_contacts.
 381       */
 382      public function test_delete_contacts() {
 383          $this->resetAfterTest(true);
 384  
 385          $user1 = self::getDataGenerator()->create_user();
 386          $user2 = self::getDataGenerator()->create_user();
 387          $user3 = self::getDataGenerator()->create_user();
 388          $user4 = self::getDataGenerator()->create_user();
 389          $user5 = self::getDataGenerator()->create_user();
 390          $user6 = self::getDataGenerator()->create_user();
 391          $this->setUser($user1);
 392  
 393          \core_message\api::add_contact($user1->id, $user3->id);
 394          \core_message\api::add_contact($user1->id, $user4->id);
 395          \core_message\api::add_contact($user1->id, $user5->id);
 396          \core_message\api::add_contact($user1->id, $user6->id);
 397  
 398          // Removing a non-contact.
 399          $return = core_message_external::delete_contacts(array($user2->id));
 400          $this->assertNull($return);
 401  
 402          // Removing one contact.
 403          $return = core_message_external::delete_contacts(array($user3->id));
 404          $this->assertNull($return);
 405  
 406          // Removing multiple contacts.
 407          $return = core_message_external::delete_contacts(array($user4->id, $user5->id));
 408          $this->assertNull($return);
 409  
 410          // Removing contact from unexisting user.
 411          $return = core_message_external::delete_contacts(array(99999));
 412          $this->assertNull($return);
 413  
 414          // Removing mixed valid and invalid data.
 415          $return = core_message_external::delete_contacts(array($user6->id, 99999));
 416          $this->assertNull($return);
 417  
 418          // Try to delete a contact of another user contact list, should throw an exception.
 419          // All assertions must be added before this point.
 420          $this->expectException('required_capability_exception');
 421          core_message_external::delete_contacts(array($user2->id), $user3->id);
 422      }
 423  
 424      /**
 425       * Test getting contact requests.
 426       */
 427      public function test_get_contact_requests() {
 428          global $PAGE;
 429  
 430          $this->resetAfterTest();
 431  
 432          $user1 = self::getDataGenerator()->create_user();
 433          $user2 = self::getDataGenerator()->create_user();
 434          $user3 = self::getDataGenerator()->create_user();
 435  
 436          $this->setUser($user1);
 437  
 438          // Block one user, their request should not show up.
 439          \core_message\api::block_user($user1->id, $user3->id);
 440  
 441          \core_message\api::create_contact_request($user2->id, $user1->id);
 442          \core_message\api::create_contact_request($user3->id, $user1->id);
 443  
 444          $requests = core_message_external::get_contact_requests($user1->id);
 445          $requests = \external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 446  
 447          $this->assertCount(1, $requests);
 448  
 449          $request = reset($requests);
 450          $userpicture = new \user_picture($user2);
 451          $profileimageurl = $userpicture->get_url($PAGE)->out(false);
 452  
 453          $this->assertEquals($user2->id, $request['id']);
 454          $this->assertEquals(fullname($user2), $request['fullname']);
 455          $this->assertArrayHasKey('profileimageurl', $request);
 456          $this->assertArrayHasKey('profileimageurlsmall', $request);
 457          $this->assertArrayHasKey('isonline', $request);
 458          $this->assertArrayHasKey('showonlinestatus', $request);
 459          $this->assertArrayHasKey('isblocked', $request);
 460          $this->assertArrayHasKey('iscontact', $request);
 461      }
 462  
 463      /**
 464       * Test the get_contact_requests() function when the user has blocked the sender of the request.
 465       */
 466      public function test_get_contact_requests_blocked_sender() {
 467          $this->resetAfterTest();
 468          $user1 = self::getDataGenerator()->create_user();
 469          $user2 = self::getDataGenerator()->create_user();
 470  
 471          // User1 blocks User2.
 472          \core_message\api::block_user($user1->id, $user2->id);
 473  
 474          // User2 tries to add User1 as a contact.
 475          \core_message\api::create_contact_request($user2->id, $user1->id);
 476  
 477          // Verify we don't see the contact request from the blocked user User2 in the requests for User1.
 478          $this->setUser($user1);
 479          $requests = core_message_external::get_contact_requests($user1->id);
 480          $requests = \external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 481  
 482          $this->assertCount(0, $requests);
 483      }
 484  
 485      /**
 486       * Test getting contact requests when there are none.
 487       */
 488      public function test_get_contact_requests_no_requests() {
 489          $this->resetAfterTest();
 490  
 491          $user1 = self::getDataGenerator()->create_user();
 492  
 493          $this->setUser($user1);
 494  
 495          $requests = core_message_external::get_contact_requests($user1->id);
 496          $requests = \external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 497  
 498          $this->assertEmpty($requests);
 499      }
 500  
 501      /**
 502       * Test getting contact requests with limits.
 503       */
 504      public function test_get_contact_requests_with_limits() {
 505          $this->resetAfterTest();
 506  
 507          $user1 = self::getDataGenerator()->create_user();
 508          $user2 = self::getDataGenerator()->create_user();
 509          $user3 = self::getDataGenerator()->create_user();
 510  
 511          $this->setUser($user1);
 512  
 513          \core_message\api::create_contact_request($user2->id, $user1->id);
 514          \core_message\api::create_contact_request($user3->id, $user1->id);
 515  
 516          $requests = core_message_external::get_contact_requests($user1->id, 0, 1);
 517          $requests = \external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 518  
 519          $this->assertCount(1, $requests);
 520      }
 521  
 522      /**
 523       * Test getting contact requests with messaging disabled.
 524       */
 525      public function test_get_contact_requests_messaging_disabled() {
 526          global $CFG;
 527  
 528          $this->resetAfterTest();
 529  
 530          // Create some skeleton data just so we can call the WS.
 531          $user1 = self::getDataGenerator()->create_user();
 532  
 533          $this->setUser($user1);
 534  
 535          // Disable messaging.
 536          $CFG->messaging = 0;
 537  
 538          // Ensure an exception is thrown.
 539          $this->expectException('moodle_exception');
 540          core_message_external::get_contact_requests($user1->id);
 541      }
 542  
 543      /**
 544       * Test getting contact requests with no permission.
 545       */
 546      public function test_get_contact_requests_no_permission() {
 547          $this->resetAfterTest();
 548  
 549          // Create some skeleton data just so we can call the WS.
 550          $user1 = self::getDataGenerator()->create_user();
 551          $user2 = self::getDataGenerator()->create_user();
 552          $user3 = self::getDataGenerator()->create_user();
 553  
 554          $this->setUser($user3);
 555  
 556          // Ensure an exception is thrown.
 557          $this->expectException('required_capability_exception');
 558          core_message_external::create_contact_request($user1->id, $user2->id);
 559      }
 560  
 561      /**
 562       * Test getting the number of received contact requests.
 563       */
 564      public function test_get_received_contact_requests_count() {
 565          $this->resetAfterTest();
 566  
 567          $user1 = self::getDataGenerator()->create_user();
 568          $user2 = self::getDataGenerator()->create_user();
 569          $user3 = self::getDataGenerator()->create_user();
 570          $user4 = self::getDataGenerator()->create_user();
 571  
 572          $this->setUser($user1);
 573  
 574          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 575          $contactrequestnumber = \external_api::clean_returnvalue(
 576              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 577          $this->assertEquals(0, $contactrequestnumber);
 578  
 579          \core_message\api::create_contact_request($user2->id, $user1->id);
 580  
 581          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 582          $contactrequestnumber = \external_api::clean_returnvalue(
 583              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 584          $this->assertEquals(1, $contactrequestnumber);
 585  
 586          \core_message\api::create_contact_request($user3->id, $user1->id);
 587  
 588          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 589          $contactrequestnumber = \external_api::clean_returnvalue(
 590              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 591          $this->assertEquals(2, $contactrequestnumber);
 592  
 593          \core_message\api::create_contact_request($user1->id, $user4->id);
 594  
 595          // Web service should ignore sent requests.
 596          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 597          $contactrequestnumber = \external_api::clean_returnvalue(
 598              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 599          $this->assertEquals(2, $contactrequestnumber);
 600      }
 601  
 602      /**
 603       * Test the get_received_contact_requests_count() function when the user has blocked the sender of the request.
 604       */
 605      public function test_get_received_contact_requests_count_blocked_sender() {
 606          $this->resetAfterTest();
 607          $user1 = self::getDataGenerator()->create_user();
 608          $user2 = self::getDataGenerator()->create_user();
 609  
 610          // User1 blocks User2.
 611          \core_message\api::block_user($user1->id, $user2->id);
 612  
 613          // User2 tries to add User1 as a contact.
 614          \core_message\api::create_contact_request($user2->id, $user1->id);
 615  
 616          // Verify we don't see the contact request from the blocked user User2 in the count for User1.
 617          $this->setUser($user1);
 618          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 619          $contactrequestnumber = \external_api::clean_returnvalue(
 620              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 621          $this->assertEquals(0, $contactrequestnumber);
 622      }
 623  
 624      /**
 625       * Test getting the number of received contact requests with no permissions.
 626       */
 627      public function test_get_received_contact_requests_count_no_permission() {
 628          $this->resetAfterTest();
 629  
 630          // Create some skeleton data just so we can call the WS.
 631          $user1 = self::getDataGenerator()->create_user();
 632          $user2 = self::getDataGenerator()->create_user();
 633  
 634          $this->setUser($user2);
 635  
 636          // Ensure an exception is thrown.
 637          $this->expectException('required_capability_exception');
 638          core_message_external::get_received_contact_requests_count($user1->id);
 639      }
 640  
 641      /**
 642       * Test getting the number of received contact requests with messaging disabled.
 643       */
 644      public function test_get_received_contact_requests_count_messaging_disabled() {
 645          global $CFG;
 646  
 647          $this->resetAfterTest();
 648  
 649          // Create some skeleton data just so we can call the WS.
 650          $user1 = self::getDataGenerator()->create_user();
 651  
 652          $this->setUser($user1);
 653  
 654          // Disable messaging.
 655          $CFG->messaging = 0;
 656  
 657          // Ensure an exception is thrown.
 658          $this->expectException('moodle_exception');
 659          core_message_external::get_received_contact_requests_count($user1->id);
 660      }
 661  
 662      /**
 663       * Test creating a contact request.
 664       */
 665      public function test_create_contact_request() {
 666          global $CFG, $DB;
 667  
 668          $this->resetAfterTest();
 669  
 670          $user1 = self::getDataGenerator()->create_user();
 671          $user2 = self::getDataGenerator()->create_user();
 672  
 673          $this->setUser($user1);
 674  
 675          // Allow users to message anyone site-wide.
 676          $CFG->messagingallusers = 1;
 677  
 678          $return = core_message_external::create_contact_request($user1->id, $user2->id);
 679          $return = \external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
 680          $this->assertEquals([], $return['warnings']);
 681  
 682          $request = $DB->get_records('message_contact_requests');
 683  
 684          $this->assertCount(1, $request);
 685  
 686          $request = reset($request);
 687  
 688          $this->assertEquals($request->id, $return['request']['id']);
 689          $this->assertEquals($request->userid, $return['request']['userid']);
 690          $this->assertEquals($request->requesteduserid, $return['request']['requesteduserid']);
 691          $this->assertEquals($request->timecreated, $return['request']['timecreated']);
 692      }
 693  
 694      /**
 695       * Test creating a contact request when not allowed.
 696       */
 697      public function test_create_contact_request_not_allowed() {
 698          global $CFG;
 699  
 700          $this->resetAfterTest();
 701  
 702          $user1 = self::getDataGenerator()->create_user();
 703          $user2 = self::getDataGenerator()->create_user();
 704  
 705          $this->setUser($user1);
 706  
 707          $CFG->messagingallusers = 0;
 708  
 709          $return = core_message_external::create_contact_request($user1->id, $user2->id);
 710          $return = \external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
 711  
 712          $warning = reset($return['warnings']);
 713  
 714          $this->assertEquals('user', $warning['item']);
 715          $this->assertEquals($user2->id, $warning['itemid']);
 716          $this->assertEquals('cannotcreatecontactrequest', $warning['warningcode']);
 717          $this->assertEquals('You are unable to create a contact request for this user', $warning['message']);
 718      }
 719  
 720      /**
 721       * Test creating a contact request with messaging disabled.
 722       */
 723      public function test_create_contact_request_messaging_disabled() {
 724          global $CFG;
 725  
 726          $this->resetAfterTest();
 727  
 728          // Create some skeleton data just so we can call the WS.
 729          $user1 = self::getDataGenerator()->create_user();
 730          $user2 = self::getDataGenerator()->create_user();
 731  
 732          $this->setUser($user1);
 733  
 734          // Disable messaging.
 735          $CFG->messaging = 0;
 736  
 737          // Ensure an exception is thrown.
 738          $this->expectException('moodle_exception');
 739          core_message_external::create_contact_request($user1->id, $user2->id);
 740      }
 741  
 742      /**
 743       * Test creating a contact request with no permission.
 744       */
 745      public function test_create_contact_request_no_permission() {
 746          $this->resetAfterTest();
 747  
 748          // Create some skeleton data just so we can call the WS.
 749          $user1 = self::getDataGenerator()->create_user();
 750          $user2 = self::getDataGenerator()->create_user();
 751          $user3 = self::getDataGenerator()->create_user();
 752  
 753          $this->setUser($user3);
 754  
 755          // Ensure an exception is thrown.
 756          $this->expectException('required_capability_exception');
 757          core_message_external::create_contact_request($user1->id, $user2->id);
 758      }
 759  
 760      /**
 761       * Test confirming a contact request.
 762       */
 763      public function test_confirm_contact_request() {
 764          global $DB;
 765  
 766          $this->resetAfterTest();
 767  
 768          $user1 = self::getDataGenerator()->create_user();
 769          $user2 = self::getDataGenerator()->create_user();
 770  
 771          $this->setUser($user1);
 772  
 773          \core_message\api::create_contact_request($user1->id, $user2->id);
 774  
 775          $this->setUser($user2);
 776  
 777          $return = core_message_external::confirm_contact_request($user1->id, $user2->id);
 778          $return = \external_api::clean_returnvalue(core_message_external::confirm_contact_request_returns(), $return);
 779          $this->assertEquals(array(), $return);
 780  
 781          $this->assertEquals(0, $DB->count_records('message_contact_requests'));
 782  
 783          $contact = $DB->get_records('message_contacts');
 784  
 785          $this->assertCount(1, $contact);
 786  
 787          $contact = reset($contact);
 788  
 789          $this->assertEquals($user1->id, $contact->userid);
 790          $this->assertEquals($user2->id, $contact->contactid);
 791      }
 792  
 793      /**
 794       * Test confirming a contact request with messaging disabled.
 795       */
 796      public function test_confirm_contact_request_messaging_disabled() {
 797          global $CFG;
 798  
 799          $this->resetAfterTest();
 800  
 801          // Create some skeleton data just so we can call the WS.
 802          $user1 = self::getDataGenerator()->create_user();
 803          $user2 = self::getDataGenerator()->create_user();
 804  
 805          $this->setUser($user1);
 806  
 807          // Disable messaging.
 808          $CFG->messaging = 0;
 809  
 810          // Ensure an exception is thrown.
 811          $this->expectException('moodle_exception');
 812          core_message_external::confirm_contact_request($user1->id, $user2->id);
 813      }
 814  
 815      /**
 816       * Test confirming a contact request with no permission.
 817       */
 818      public function test_confirm_contact_request_no_permission() {
 819          $this->resetAfterTest();
 820  
 821          // Create some skeleton data just so we can call the WS.
 822          $user1 = self::getDataGenerator()->create_user();
 823          $user2 = self::getDataGenerator()->create_user();
 824          $user3 = self::getDataGenerator()->create_user();
 825  
 826          $this->setUser($user3);
 827  
 828          // Ensure an exception is thrown.
 829          $this->expectException('required_capability_exception');
 830          core_message_external::confirm_contact_request($user1->id, $user2->id);
 831      }
 832  
 833      /**
 834       * Test declining a contact request.
 835       */
 836      public function test_decline_contact_request() {
 837          global $DB;
 838  
 839          $this->resetAfterTest();
 840  
 841          $user1 = self::getDataGenerator()->create_user();
 842          $user2 = self::getDataGenerator()->create_user();
 843  
 844          $this->setUser($user1);
 845  
 846          \core_message\api::create_contact_request($user1->id, $user2->id);
 847  
 848          $this->setUser($user2);
 849  
 850          $return = core_message_external::decline_contact_request($user1->id, $user2->id);
 851          $return = \external_api::clean_returnvalue(core_message_external::decline_contact_request_returns(), $return);
 852          $this->assertEquals(array(), $return);
 853  
 854          $this->assertEquals(0, $DB->count_records('message_contact_requests'));
 855          $this->assertEquals(0, $DB->count_records('message_contacts'));
 856      }
 857  
 858      /**
 859       * Test declining a contact request with messaging disabled.
 860       */
 861      public function test_decline_contact_request_messaging_disabled() {
 862          global $CFG;
 863  
 864          $this->resetAfterTest();
 865  
 866          // Create some skeleton data just so we can call the WS.
 867          $user1 = self::getDataGenerator()->create_user();
 868          $user2 = self::getDataGenerator()->create_user();
 869  
 870          $this->setUser($user1);
 871  
 872          // Disable messaging.
 873          $CFG->messaging = 0;
 874  
 875          // Ensure an exception is thrown.
 876          $this->expectException('moodle_exception');
 877          core_message_external::decline_contact_request($user1->id, $user2->id);
 878      }
 879  
 880      /**
 881       * Test declining a contact request with no permission.
 882       */
 883      public function test_decline_contact_request_no_permission() {
 884          $this->resetAfterTest();
 885  
 886          // Create some skeleton data just so we can call the WS.
 887          $user1 = self::getDataGenerator()->create_user();
 888          $user2 = self::getDataGenerator()->create_user();
 889          $user3 = self::getDataGenerator()->create_user();
 890  
 891          $this->setUser($user3);
 892  
 893          // Ensure an exception is thrown.
 894          $this->expectException('required_capability_exception');
 895          core_message_external::decline_contact_request($user1->id, $user2->id);
 896      }
 897  
 898      /**
 899       * Test muting conversations.
 900       */
 901      public function test_mute_conversations() {
 902          global $DB;
 903  
 904          $this->resetAfterTest(true);
 905  
 906          $user1 = self::getDataGenerator()->create_user();
 907          $user2 = self::getDataGenerator()->create_user();
 908  
 909          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 910              [$user1->id, $user2->id]);
 911  
 912          $this->setUser($user1);
 913  
 914          // Muting a conversation.
 915          $return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
 916          $return = \external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
 917          $this->assertEquals(array(), $return);
 918  
 919          // Get list of muted conversations.
 920          $mca = $DB->get_record('message_conversation_actions', []);
 921  
 922          $this->assertEquals($user1->id, $mca->userid);
 923          $this->assertEquals($conversation->id, $mca->conversationid);
 924          $this->assertEquals(\core_message\api::CONVERSATION_ACTION_MUTED, $mca->action);
 925  
 926          // Muting a conversation that is already muted.
 927          $return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
 928          $return = \external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
 929          $this->assertEquals(array(), $return);
 930  
 931          $this->assertEquals(1, $DB->count_records('message_conversation_actions'));
 932      }
 933  
 934      /**
 935       * Test muting a conversation with messaging disabled.
 936       */
 937      public function test_mute_conversations_messaging_disabled() {
 938          global $CFG;
 939  
 940          $this->resetAfterTest();
 941  
 942          // Create some skeleton data just so we can call the WS.
 943          $user1 = self::getDataGenerator()->create_user();
 944          $user2 = self::getDataGenerator()->create_user();
 945  
 946          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 947              [$user1->id, $user2->id]);
 948  
 949          $this->setUser($user1);
 950  
 951          // Disable messaging.
 952          $CFG->messaging = 0;
 953  
 954          // Ensure an exception is thrown.
 955          $this->expectException('moodle_exception');
 956          core_message_external::mute_conversations($user1->id, [$conversation->id]);
 957      }
 958  
 959      /**
 960       * Test muting a conversation with no permission.
 961       */
 962      public function test_mute_conversations_no_permission() {
 963          $this->resetAfterTest();
 964  
 965          // Create some skeleton data just so we can call the WS.
 966          $user1 = self::getDataGenerator()->create_user();
 967          $user2 = self::getDataGenerator()->create_user();
 968          $user3 = self::getDataGenerator()->create_user();
 969  
 970          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 971              [$user1->id, $user2->id]);
 972  
 973          $this->setUser($user3);
 974  
 975          // Ensure an exception is thrown.
 976          $this->expectException('required_capability_exception');
 977          core_message_external::mute_conversations($user1->id, [$conversation->id]);
 978      }
 979  
 980      /**
 981       * Test unmuting conversations.
 982       */
 983      public function test_unmute_conversations() {
 984          global $DB;
 985  
 986          $this->resetAfterTest(true);
 987  
 988          $user1 = self::getDataGenerator()->create_user();
 989          $user2 = self::getDataGenerator()->create_user();
 990  
 991          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 992              [$user1->id, $user2->id]);
 993  
 994          $this->setUser($user1);
 995  
 996          // Mute the conversation.
 997          \core_message\api::mute_conversation($user1->id, $conversation->id);
 998  
 999          // Unmuting a conversation.
1000          $return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1001          $return = \external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
1002          $this->assertEquals(array(), $return);
1003  
1004          $this->assertEquals(0, $DB->count_records('message_conversation_actions'));
1005  
1006          // Unmuting a conversation which is already unmuted.
1007          $return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1008          $return = \external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
1009          $this->assertEquals(array(), $return);
1010  
1011          $this->assertEquals(0, $DB->count_records('message_conversation_actions'));
1012      }
1013  
1014      /**
1015       * Test unmuting a conversation with messaging disabled.
1016       */
1017      public function test_unmute_conversation_messaging_disabled() {
1018          global $CFG;
1019  
1020          $this->resetAfterTest();
1021  
1022          // Create some skeleton data just so we can call the WS.
1023          $user1 = self::getDataGenerator()->create_user();
1024          $user2 = self::getDataGenerator()->create_user();
1025  
1026          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1027              [$user1->id, $user2->id]);
1028  
1029          $this->setUser($user1);
1030  
1031          // Disable messaging.
1032          $CFG->messaging = 0;
1033  
1034          // Ensure an exception is thrown.
1035          $this->expectException('moodle_exception');
1036          core_message_external::unmute_conversations($user1->id, [$user2->id]);
1037      }
1038  
1039      /**
1040       * Test unmuting a conversation with no permission.
1041       */
1042      public function test_unmute_conversation_no_permission() {
1043          $this->resetAfterTest();
1044  
1045          // Create some skeleton data just so we can call the WS.
1046          $user1 = self::getDataGenerator()->create_user();
1047          $user2 = self::getDataGenerator()->create_user();
1048          $user3 = self::getDataGenerator()->create_user();
1049  
1050          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1051              [$user1->id, $user2->id]);
1052  
1053          $this->setUser($user3);
1054  
1055          // Ensure an exception is thrown.
1056          $this->expectException('required_capability_exception');
1057          core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1058      }
1059  
1060      /**
1061       * Test blocking a user.
1062       */
1063      public function test_block_user() {
1064          global $DB;
1065  
1066          $this->resetAfterTest(true);
1067  
1068          $user1 = self::getDataGenerator()->create_user();
1069          $user2 = self::getDataGenerator()->create_user();
1070  
1071          $this->setUser($user1);
1072  
1073          // Blocking a user.
1074          $return = core_message_external::block_user($user1->id, $user2->id);
1075          $return = \external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1076          $this->assertEquals(array(), $return);
1077  
1078          // Get list of blocked users.
1079          $record = $DB->get_record('message_users_blocked', []);
1080  
1081          $this->assertEquals($user1->id, $record->userid);
1082          $this->assertEquals($user2->id, $record->blockeduserid);
1083  
1084          // Blocking a user who is already blocked.
1085          $return = core_message_external::block_user($user1->id, $user2->id);
1086          $return = \external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1087          $this->assertEquals(array(), $return);
1088  
1089          $this->assertEquals(1, $DB->count_records('message_users_blocked'));
1090      }
1091  
1092      /**
1093       * Test blocking a user.
1094       */
1095      public function test_block_user_when_ineffective() {
1096          global $DB;
1097  
1098          $this->resetAfterTest(true);
1099  
1100          $user1 = self::getDataGenerator()->create_user();
1101          $user2 = self::getDataGenerator()->create_user();
1102  
1103          $this->setUser($user1);
1104  
1105          $authenticateduser = $DB->get_record('role', array('shortname' => 'user'));
1106          assign_capability('moodle/site:messageanyuser', CAP_ALLOW, $authenticateduser->id, \context_system::instance(), true);
1107  
1108          // Blocking a user.
1109          $return = core_message_external::block_user($user1->id, $user2->id);
1110          $return = \external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1111          $this->assertEquals(array(), $return);
1112  
1113          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1114      }
1115  
1116      /**
1117       * Test blocking a user with messaging disabled.
1118       */
1119      public function test_block_user_messaging_disabled() {
1120          global $CFG;
1121  
1122          $this->resetAfterTest();
1123  
1124          // Create some skeleton data just so we can call the WS.
1125          $user1 = self::getDataGenerator()->create_user();
1126          $user2 = self::getDataGenerator()->create_user();
1127  
1128          $this->setUser($user1);
1129  
1130          // Disable messaging.
1131          $CFG->messaging = 0;
1132  
1133          // Ensure an exception is thrown.
1134          $this->expectException('moodle_exception');
1135          core_message_external::block_user($user1->id, $user2->id);
1136      }
1137  
1138      /**
1139       * Test blocking a user with no permission.
1140       */
1141      public function test_block_user_no_permission() {
1142          $this->resetAfterTest();
1143  
1144          // Create some skeleton data just so we can call the WS.
1145          $user1 = self::getDataGenerator()->create_user();
1146          $user2 = self::getDataGenerator()->create_user();
1147          $user3 = self::getDataGenerator()->create_user();
1148  
1149          $this->setUser($user3);
1150  
1151          // Ensure an exception is thrown.
1152          $this->expectException('required_capability_exception');
1153          core_message_external::block_user($user1->id, $user2->id);
1154      }
1155  
1156      /**
1157       * Test unblocking a user.
1158       */
1159      public function test_unblock_user() {
1160          global $DB;
1161  
1162          $this->resetAfterTest(true);
1163  
1164          $user1 = self::getDataGenerator()->create_user();
1165          $user2 = self::getDataGenerator()->create_user();
1166  
1167          $this->setUser($user1);
1168  
1169          // Block the user.
1170          \core_message\api::block_user($user1->id, $user2->id);
1171  
1172          // Unblocking a user.
1173          $return = core_message_external::unblock_user($user1->id, $user2->id);
1174          $return = \external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
1175          $this->assertEquals(array(), $return);
1176  
1177          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1178  
1179          // Unblocking a user who is already unblocked.
1180          $return = core_message_external::unblock_user($user1->id, $user2->id);
1181          $return = \external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
1182          $this->assertEquals(array(), $return);
1183  
1184          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1185      }
1186  
1187      /**
1188       * Test unblocking a user with messaging disabled.
1189       */
1190      public function test_unblock_user_messaging_disabled() {
1191          global $CFG;
1192  
1193          $this->resetAfterTest();
1194  
1195          // Create some skeleton data just so we can call the WS.
1196          $user1 = self::getDataGenerator()->create_user();
1197          $user2 = self::getDataGenerator()->create_user();
1198  
1199          $this->setUser($user1);
1200  
1201          // Disable messaging.
1202          $CFG->messaging = 0;
1203  
1204          // Ensure an exception is thrown.
1205          $this->expectException('moodle_exception');
1206          core_message_external::unblock_user($user1->id, $user2->id);
1207      }
1208  
1209      /**
1210       * Test unblocking a user with no permission.
1211       */
1212      public function test_unblock_user_no_permission() {
1213          $this->resetAfterTest();
1214  
1215          // Create some skeleton data just so we can call the WS.
1216          $user1 = self::getDataGenerator()->create_user();
1217          $user2 = self::getDataGenerator()->create_user();
1218          $user3 = self::getDataGenerator()->create_user();
1219  
1220          $this->setUser($user3);
1221  
1222          // Ensure an exception is thrown.
1223          $this->expectException('required_capability_exception');
1224          core_message_external::unblock_user($user1->id, $user2->id);
1225      }
1226  
1227      /**
1228       * Test search_contacts.
1229       */
1230      public function test_search_contacts() {
1231          global $DB;
1232          $this->resetAfterTest(true);
1233  
1234          $course1 = $this->getDataGenerator()->create_course();
1235          $course2 = $this->getDataGenerator()->create_course();
1236  
1237          $user1 = new \stdClass();
1238          $user1->firstname = 'X';
1239          $user1->lastname = 'X';
1240          $user1 = $this->getDataGenerator()->create_user($user1);
1241          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
1242          $this->getDataGenerator()->enrol_user($user1->id, $course2->id);
1243  
1244          $user2 = new \stdClass();
1245          $user2->firstname = 'Eric';
1246          $user2->lastname = 'Cartman';
1247          $user2 = self::getDataGenerator()->create_user($user2);
1248          $user3 = new \stdClass();
1249          $user3->firstname = 'Stan';
1250          $user3->lastname = 'Marsh';
1251          $user3 = self::getDataGenerator()->create_user($user3);
1252          self::getDataGenerator()->enrol_user($user3->id, $course1->id);
1253          $user4 = new \stdClass();
1254          $user4->firstname = 'Kyle';
1255          $user4->lastname = 'Broflovski';
1256          $user4 = self::getDataGenerator()->create_user($user4);
1257          $user5 = new \stdClass();
1258          $user5->firstname = 'Kenny';
1259          $user5->lastname = 'McCormick';
1260          $user5 = self::getDataGenerator()->create_user($user5);
1261          self::getDataGenerator()->enrol_user($user5->id, $course2->id);
1262  
1263          $this->setUser($user1);
1264  
1265          $results = core_message_external::search_contacts('r');
1266          $results = \external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1267          $this->assertCount(5, $results); // Users 2 through 5 + admin
1268  
1269          $results = core_message_external::search_contacts('r', true);
1270          $results = \external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1271          $this->assertCount(2, $results);
1272  
1273          $results = core_message_external::search_contacts('Kyle', false);
1274          $results = \external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1275          $this->assertCount(1, $results);
1276          $result = reset($results);
1277          $this->assertEquals($user4->id, $result['id']);
1278  
1279          $results = core_message_external::search_contacts('y', false);
1280          $results = \external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1281          $this->assertCount(2, $results);
1282  
1283          $results = core_message_external::search_contacts('y', true);
1284          $results = \external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1285          $this->assertCount(1, $results);
1286          $result = reset($results);
1287          $this->assertEquals($user5->id, $result['id']);
1288  
1289          // Empty query, will throw an exception.
1290          $this->expectException(\moodle_exception::class);
1291          $results = core_message_external::search_contacts('');
1292      }
1293  
1294      /**
1295       * Test get_messages.
1296       */
1297      public function test_get_messages() {
1298          global $CFG, $DB;
1299          $this->resetAfterTest(true);
1300  
1301          $this->preventResetByRollback();
1302          // This mark the messages as read!.
1303          $sink = $this->redirectMessages();
1304  
1305          $user1 = self::getDataGenerator()->create_user();
1306          $user2 = self::getDataGenerator()->create_user();
1307          $user3 = self::getDataGenerator()->create_user();
1308  
1309          $course = self::getDataGenerator()->create_course();
1310  
1311          // Send a message from one user to another.
1312          message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
1313          message_post_message($user1, $user3, 'some random text 2', FORMAT_MOODLE);
1314          message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
1315          message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
1316          message_post_message($user3, $user1, 'some random text 5', FORMAT_MOODLE);
1317  
1318          $this->setUser($user1);
1319          // Get read conversations from user1 to user2.
1320          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', true, true, 0, 0);
1321          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1322          $this->assertCount(1, $messages['messages']);
1323  
1324          // Delete the message.
1325          $message = array_shift($messages['messages']);
1326          \core_message\api::delete_message($user1->id, $message['id']);
1327  
1328          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', true, true, 0, 0);
1329          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1330          $this->assertCount(0, $messages['messages']);
1331  
1332          // Get unread conversations from user1 to user2.
1333          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', false, true, 0, 0);
1334          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1335          $this->assertCount(0, $messages['messages']);
1336  
1337          // Get read messages send from user1.
1338          $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
1339          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1340          $this->assertCount(1, $messages['messages']);
1341  
1342          $this->setUser($user2);
1343          // Get read conversations from any user to user2.
1344          $messages = core_message_external::get_messages($user2->id, 0, 'conversations', true, true, 0, 0);
1345          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1346          $this->assertCount(2, $messages['messages']);
1347  
1348          // Conversations from user3 to user2.
1349          $messages = core_message_external::get_messages($user2->id, $user3->id, 'conversations', true, true, 0, 0);
1350          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1351          $this->assertCount(1, $messages['messages']);
1352  
1353          // Delete the message.
1354          $message = array_shift($messages['messages']);
1355          \core_message\api::delete_message($user2->id, $message['id']);
1356  
1357          $messages = core_message_external::get_messages($user2->id, $user3->id, 'conversations', true, true, 0, 0);
1358          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1359          $this->assertCount(0, $messages['messages']);
1360  
1361          $this->setUser($user3);
1362          // Get read notifications received by user3.
1363          $messages = core_message_external::get_messages($user3->id, 0, 'notifications', true, true, 0, 0);
1364          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1365          $this->assertCount(0, $messages['messages']);
1366  
1367          // Now, create some notifications...
1368          // We are creating fake notifications but based on real ones.
1369  
1370          // This one comes from a disabled plugin's provider and therefore is not sent.
1371          $eventdata = new \core\message\message();
1372          $eventdata->courseid          = $course->id;
1373          $eventdata->notification      = 1;
1374          $eventdata->modulename        = 'moodle';
1375          $eventdata->component         = 'enrol_paypal';
1376          $eventdata->name              = 'paypal_enrolment';
1377          $eventdata->userfrom          = get_admin();
1378          $eventdata->userto            = $user1;
1379          $eventdata->subject           = "Moodle: PayPal payment";
1380          $eventdata->fullmessage       = "Your PayPal payment is pending.";
1381          $eventdata->fullmessageformat = FORMAT_PLAIN;
1382          $eventdata->fullmessagehtml   = '';
1383          $eventdata->smallmessage      = '';
1384          message_send($eventdata);
1385          $this->assertDebuggingCalled('Attempt to send msg from a provider enrol_paypal/paypal_enrolment '.
1386              'that is inactive or not allowed for the user id='.$user1->id);
1387  
1388          // This one omits notification = 1.
1389          $message = new \core\message\message();
1390          $message->courseid          = $course->id;
1391          $message->component         = 'enrol_manual';
1392          $message->name              = 'expiry_notification';
1393          $message->userfrom          = $user2;
1394          $message->userto            = $user1;
1395          $message->subject           = 'Test: This is not a notification but otherwise is valid';
1396          $message->fullmessage       = 'Test: Full message';
1397          $message->fullmessageformat = FORMAT_MARKDOWN;
1398          $message->fullmessagehtml   = markdown_to_html($message->fullmessage);
1399          $message->smallmessage      = $message->subject;
1400          $message->contexturlname    = $course->fullname;
1401          $message->contexturl        = (string)new \moodle_url('/course/view.php', array('id' => $course->id));
1402          message_send($message);
1403  
1404          $message = new \core\message\message();
1405          $message->courseid          = $course->id;
1406          $message->notification      = 1;
1407          $message->component         = 'enrol_manual';
1408          $message->name              = 'expiry_notification';
1409          $message->userfrom          = $user2;
1410          $message->userto            = $user1;
1411          $message->subject           = 'Enrolment expired';
1412          $message->fullmessage       = 'Enrolment expired blah blah blah';
1413          $message->fullmessageformat = FORMAT_MARKDOWN;
1414          $message->fullmessagehtml   = markdown_to_html($message->fullmessage);
1415          $message->smallmessage      = $message->subject;
1416          $message->contexturlname    = $course->fullname;
1417          $message->contexturl        = (string)new \moodle_url('/course/view.php', array('id' => $course->id));
1418          message_send($message);
1419  
1420          $userfrom = \core_user::get_noreply_user();
1421          $userfrom->maildisplay = true;
1422          $eventdata = new \core\message\message();
1423          $eventdata->courseid          = $course->id;
1424          $eventdata->component         = 'moodle';
1425          $eventdata->name              = 'badgecreatornotice';
1426          $eventdata->userfrom          = $userfrom;
1427          $eventdata->userto            = $user1;
1428          $eventdata->notification      = 1;
1429          $eventdata->subject           = 'New badge';
1430          $eventdata->fullmessage       = format_text_email($eventdata->subject, FORMAT_HTML);
1431          $eventdata->fullmessageformat = FORMAT_PLAIN;
1432          $eventdata->fullmessagehtml   = $eventdata->subject;
1433          $eventdata->smallmessage      = $eventdata->subject;
1434          message_send($eventdata);
1435  
1436          $eventdata = new \core\message\message();
1437          $eventdata->courseid         = $course->id;
1438          $eventdata->name             = 'submission';
1439          $eventdata->component        = 'mod_feedback';
1440          $eventdata->userfrom         = $user1;
1441          $eventdata->userto           = $user2;
1442          $eventdata->subject          = 'Feedback submitted';
1443          $eventdata->fullmessage      = 'Feedback submitted from an user';
1444          $eventdata->fullmessageformat = FORMAT_PLAIN;
1445          $eventdata->fullmessagehtml  = '<strong>Feedback submitted</strong>';
1446          $eventdata->smallmessage     = '';
1447          $eventdata->customdata         = ['datakey' => 'data'];
1448          message_send($eventdata);
1449  
1450          $this->setUser($user1);
1451          // Get read notifications from any user to user1.
1452          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', true, true, 0, 0);
1453          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1454          $this->assertCount(3, $messages['messages']);
1455  
1456          // Get one read notifications from any user to user1.
1457          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', true, true, 0, 1);
1458          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1459          $this->assertCount(1, $messages['messages']);
1460  
1461          // Get unread notifications from any user to user1.
1462          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', false, true, 0, 0);
1463          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1464          $this->assertCount(0, $messages['messages']);
1465  
1466          // Get read both type of messages from any user to user1.
1467          $messages = core_message_external::get_messages($user1->id, 0, 'both', true, true, 0, 0);
1468          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1469          $this->assertCount(4, $messages['messages']);
1470  
1471          // Get read notifications from no-reply-user to user1.
1472          $messages = core_message_external::get_messages($user1->id, $userfrom->id, 'notifications', true, true, 0, 0);
1473          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1474          $this->assertCount(1, $messages['messages']);
1475  
1476          // Get notifications send by user1 to any user.
1477          $messages = core_message_external::get_messages(0, $user1->id, 'notifications', true, true, 0, 0);
1478          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1479          $this->assertCount(1, $messages['messages']);
1480          // Check we receive custom data as a unserialisable json.
1481          $this->assertObjectHasAttribute('datakey', json_decode($messages['messages'][0]['customdata']));
1482          $this->assertEquals('mod_feedback', $messages['messages'][0]['component']);
1483          $this->assertEquals('submission', $messages['messages'][0]['eventtype']);
1484  
1485          // Test warnings.
1486          $CFG->messaging = 0;
1487  
1488          $messages = core_message_external::get_messages(0, $user1->id, 'both', true, true, 0, 0);
1489          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1490          $this->assertCount(1, $messages['warnings']);
1491  
1492          // Test exceptions.
1493  
1494          // Messaging disabled.
1495          try {
1496              $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
1497              $this->fail('Exception expected due messaging disabled.');
1498          } catch (\moodle_exception $e) {
1499              $this->assertEquals('disabled', $e->errorcode);
1500          }
1501  
1502          $CFG->messaging = 1;
1503  
1504          // Invalid users.
1505          try {
1506              $messages = core_message_external::get_messages(0, 0, 'conversations', true, true, 0, 0);
1507              $this->fail('Exception expected due invalid users.');
1508          } catch (\moodle_exception $e) {
1509              $this->assertEquals('accessdenied', $e->errorcode);
1510          }
1511  
1512          // Invalid user ids.
1513          try {
1514              $messages = core_message_external::get_messages(2500, 0, 'conversations', true, true, 0, 0);
1515              $this->fail('Exception expected due invalid users.');
1516          } catch (\moodle_exception $e) {
1517              $this->assertEquals('invaliduser', $e->errorcode);
1518          }
1519  
1520          // Invalid users (permissions).
1521          $this->setUser($user2);
1522          try {
1523              $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
1524              $this->fail('Exception expected due invalid user.');
1525          } catch (\moodle_exception $e) {
1526              $this->assertEquals('accessdenied', $e->errorcode);
1527          }
1528  
1529      }
1530  
1531      /**
1532       * Test get_messages where we want all messages from a user, sent to any user.
1533       */
1534      public function test_get_messages_useridto_all() {
1535          $this->resetAfterTest(true);
1536  
1537          $user1 = self::getDataGenerator()->create_user();
1538          $user2 = self::getDataGenerator()->create_user();
1539          $user3 = self::getDataGenerator()->create_user();
1540  
1541          $this->setUser($user1);
1542  
1543          // Send a message from user 1 to two other users.
1544          $this->send_message($user1, $user2, 'some random text 1', 0, 1);
1545          $this->send_message($user1, $user3, 'some random text 2', 0, 2);
1546  
1547          // Get messages sent from user 1.
1548          $messages = core_message_external::get_messages(0, $user1->id, 'conversations', false, false, 0, 0);
1549          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1550  
1551          // Confirm the data is correct.
1552          $messages = $messages['messages'];
1553          $this->assertCount(2, $messages);
1554  
1555          $message1 = array_shift($messages);
1556          $message2 = array_shift($messages);
1557  
1558          $this->assertEquals($user1->id, $message1['useridfrom']);
1559          $this->assertEquals($user2->id, $message1['useridto']);
1560  
1561          $this->assertEquals($user1->id, $message2['useridfrom']);
1562          $this->assertEquals($user3->id, $message2['useridto']);
1563      }
1564  
1565      /**
1566       * Test get_messages where we want all messages to a user, sent by any user.
1567       */
1568      public function test_get_messages_useridfrom_all() {
1569          $this->resetAfterTest();
1570  
1571          $user1 = self::getDataGenerator()->create_user();
1572          $user2 = self::getDataGenerator()->create_user();
1573          $user3 = self::getDataGenerator()->create_user();
1574  
1575          $this->setUser($user1);
1576  
1577          // Send a message to user 1 from two other users.
1578          $this->send_message($user2, $user1, 'some random text 1', 0, 1);
1579          $this->send_message($user3, $user1, 'some random text 2', 0, 2);
1580  
1581          // Get messages sent to user 1.
1582          $messages = core_message_external::get_messages($user1->id, 0, 'conversations', false, false, 0, 0);
1583          $messages = \external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1584  
1585          // Confirm the data is correct.
1586          $messages = $messages['messages'];
1587          $this->assertCount(2, $messages);
1588  
1589          $message1 = array_shift($messages);
1590          $message2 = array_shift($messages);
1591  
1592          $this->assertEquals($user2->id, $message1['useridfrom']);
1593          $this->assertEquals($user1->id, $message1['useridto']);
1594  
1595          $this->assertEquals($user3->id, $message2['useridfrom']);
1596          $this->assertEquals($user1->id, $message2['useridto']);
1597      }
1598  
1599      /**
1600       * Test get_blocked_users.
1601       */
1602      public function test_get_blocked_users() {
1603          $this->resetAfterTest(true);
1604  
1605          $user1 = self::getDataGenerator()->create_user();
1606          $userstranger = self::getDataGenerator()->create_user();
1607          $useroffline1 = self::getDataGenerator()->create_user();
1608          $useroffline2 = self::getDataGenerator()->create_user();
1609          $userblocked = self::getDataGenerator()->create_user();
1610  
1611          // Login as user1.
1612          $this->setUser($user1);
1613  
1614          \core_message\api::add_contact($user1->id, $useroffline1->id);
1615          \core_message\api::add_contact($user1->id, $useroffline2->id);
1616  
1617          // The userstranger sends a couple of messages to user1.
1618          $this->send_message($userstranger, $user1, 'Hello there!');
1619          $this->send_message($userstranger, $user1, 'How you goin?');
1620  
1621          // The userblocked sends a message to user1.
1622          // Note that this user is not blocked at this point.
1623          $this->send_message($userblocked, $user1, 'Here, have some spam.');
1624  
1625          // Retrieve the list of blocked users.
1626          $this->setUser($user1);
1627          $blockedusers = core_message_external::get_blocked_users($user1->id);
1628          $blockedusers = \external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1629          $this->assertCount(0, $blockedusers['users']);
1630  
1631          // Block the $userblocked and retrieve again the list.
1632          \core_message\api::block_user($user1->id, $userblocked->id);
1633          $blockedusers = core_message_external::get_blocked_users($user1->id);
1634          $blockedusers = \external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1635          $this->assertCount(1, $blockedusers['users']);
1636  
1637          // Remove the $userblocked and check that the list now is empty.
1638          delete_user($userblocked);
1639          $blockedusers = core_message_external::get_blocked_users($user1->id);
1640          $blockedusers = \external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1641          $this->assertCount(0, $blockedusers['users']);
1642      }
1643  
1644      /**
1645       * Test mark_message_read.
1646       */
1647      public function test_mark_message_read() {
1648          $this->resetAfterTest(true);
1649  
1650          $user1 = self::getDataGenerator()->create_user();
1651          $user2 = self::getDataGenerator()->create_user();
1652          $user3 = self::getDataGenerator()->create_user();
1653  
1654          // Login as user1.
1655          $this->setUser($user1);
1656          \core_message\api::add_contact($user1->id, $user2->id);
1657          \core_message\api::add_contact($user1->id, $user3->id);
1658  
1659          // The user2 sends a couple of messages to user1.
1660          $this->send_message($user2, $user1, 'Hello there!');
1661          $this->send_message($user2, $user1, 'How you goin?');
1662          $this->send_message($user3, $user1, 'How you goin?');
1663          $this->send_message($user3, $user2, 'How you goin?');
1664  
1665          // Retrieve all messages sent by user2 (they are currently unread).
1666          $lastmessages = message_get_messages($user1->id, $user2->id, 0, false);
1667  
1668          $messageids = array();
1669          foreach ($lastmessages as $m) {
1670              $messageid = core_message_external::mark_message_read($m->id, time());
1671              $messageids[] = \external_api::clean_returnvalue(core_message_external::mark_message_read_returns(), $messageid);
1672          }
1673  
1674          // Retrieve all messages sent (they are currently read).
1675          $lastmessages = message_get_messages($user1->id, $user2->id, 0, true);
1676          $this->assertCount(2, $lastmessages);
1677          $this->assertArrayHasKey($messageids[0]['messageid'], $lastmessages);
1678          $this->assertArrayHasKey($messageids[1]['messageid'], $lastmessages);
1679  
1680          // Retrieve all messages sent by any user (that are currently unread).
1681          $lastmessages = message_get_messages($user1->id, 0, 0, false);
1682          $this->assertCount(1, $lastmessages);
1683  
1684          // Invalid message ids.
1685          try {
1686              $messageid = core_message_external::mark_message_read(1337, time());
1687              $this->fail('Exception expected due invalid messageid.');
1688          } catch (\dml_missing_record_exception $e) {
1689              $this->assertEquals('invalidrecordunknown', $e->errorcode);
1690          }
1691  
1692          // A message to a different user.
1693          $lastmessages = message_get_messages($user2->id, $user3->id, 0, false);
1694          $messageid = array_pop($lastmessages)->id;
1695          try {
1696              $messageid = core_message_external::mark_message_read($messageid, time());
1697              $this->fail('Exception expected due invalid messageid.');
1698          } catch (\invalid_parameter_exception $e) {
1699              $this->assertEquals('invalidparameter', $e->errorcode);
1700          }
1701      }
1702  
1703      /**
1704       * Test mark_notification_read.
1705       */
1706      public function test_mark_notification_read() {
1707          $this->resetAfterTest(true);
1708  
1709          $user1 = self::getDataGenerator()->create_user();
1710          $user2 = self::getDataGenerator()->create_user();
1711          $user3 = self::getDataGenerator()->create_user();
1712  
1713          // Login as user1.
1714          $this->setUser($user1);
1715          \core_message\api::add_contact($user1->id, $user2->id);
1716          \core_message\api::add_contact($user1->id, $user3->id);
1717  
1718          // The user2 sends a couple of notifications to user1.
1719          $this->send_message($user2, $user1, 'Hello there!', 1);
1720          $this->send_message($user2, $user1, 'How you goin?', 1);
1721          $this->send_message($user3, $user1, 'How you goin?', 1);
1722          $this->send_message($user3, $user2, 'How you goin?', 1);
1723  
1724          // Retrieve all notifications sent by user2 (they are currently unread).
1725          $lastnotifications = message_get_messages($user1->id, $user2->id, 1, false);
1726  
1727          $notificationids = array();
1728          foreach ($lastnotifications as $n) {
1729              $notificationid = core_message_external::mark_notification_read($n->id, time());
1730              $notificationids[] = \external_api::clean_returnvalue(core_message_external::mark_notification_read_returns(),
1731                  $notificationid);
1732          }
1733  
1734          // Retrieve all notifications sent (they are currently read).
1735          $lastnotifications = message_get_messages($user1->id, $user2->id, 1, true);
1736          $this->assertCount(2, $lastnotifications);
1737          $this->assertArrayHasKey($notificationids[1]['notificationid'], $lastnotifications);
1738          $this->assertArrayHasKey($notificationids[0]['notificationid'], $lastnotifications);
1739  
1740          // Retrieve all notifications sent by any user (that are currently unread).
1741          $lastnotifications = message_get_messages($user1->id, 0, 1, false);
1742          $this->assertCount(1, $lastnotifications);
1743  
1744          // Invalid notification ids.
1745          try {
1746              $notificationid = core_message_external::mark_notification_read(1337, time());
1747              $this->fail('Exception expected due invalid notificationid.');
1748          } catch (\dml_missing_record_exception $e) {
1749              $this->assertEquals('invalidrecord', $e->errorcode);
1750          }
1751  
1752          // A notification to a different user.
1753          $lastnotifications = message_get_messages($user2->id, $user3->id, 1, false);
1754          $notificationid = array_pop($lastnotifications)->id;
1755          try {
1756              $notificationid = core_message_external::mark_notification_read($notificationid, time());
1757              $this->fail('Exception expected due invalid notificationid.');
1758          } catch (\invalid_parameter_exception $e) {
1759              $this->assertEquals('invalidparameter', $e->errorcode);
1760          }
1761      }
1762  
1763      /**
1764       * Test delete_message.
1765       */
1766      public function test_delete_message() {
1767          global $DB;
1768          $this->resetAfterTest(true);
1769  
1770          $user1 = self::getDataGenerator()->create_user();
1771          $user2 = self::getDataGenerator()->create_user();
1772          $user3 = self::getDataGenerator()->create_user();
1773          $user4 = self::getDataGenerator()->create_user();
1774  
1775          // Login as user1.
1776          $this->setUser($user1);
1777          \core_message\api::add_contact($user1->id, $user2->id);
1778          \core_message\api::add_contact($user1->id, $user3->id);
1779  
1780          // User user1 does not interchange messages with user3.
1781          $m1to2 = message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
1782          $m2to3 = message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
1783          $m3to2 = message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
1784          $m3to4 = message_post_message($user3, $user4, 'some random text 4', FORMAT_MOODLE);
1785  
1786          // Retrieve all messages sent by user2 (they are currently unread).
1787          $lastmessages = message_get_messages($user1->id, $user2->id, 0, false);
1788  
1789          // Delete a message not read, as a user from.
1790          $result = core_message_external::delete_message($m1to2, $user1->id, false);
1791          $result = \external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1792          $this->assertTrue($result['status']);
1793          $this->assertCount(0, $result['warnings']);
1794          $mua = $DB->get_record('message_user_actions', array('messageid' => $m1to2, 'userid' => $user1->id));
1795          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua->action);
1796  
1797          // Try to delete the same message again.
1798          $result = core_message_external::delete_message($m1to2, $user1->id, false);
1799          $result = \external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1800          $this->assertFalse($result['status']);
1801  
1802          // Try to delete a message that does not belong to me.
1803          try {
1804              $messageid = core_message_external::delete_message($m2to3, $user3->id, false);
1805              $this->fail('Exception expected due invalid messageid.');
1806          } catch (\moodle_exception $e) {
1807              $this->assertEquals('You do not have permission to delete this message', $e->errorcode);
1808          }
1809  
1810          $this->setUser($user3);
1811          // Delete a message not read, as a user to.
1812          $result = core_message_external::delete_message($m2to3, $user3->id, false);
1813          $result = \external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1814          $this->assertTrue($result['status']);
1815          $this->assertCount(0, $result['warnings']);
1816          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m2to3, 'userid' => $user3->id,
1817              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
1818  
1819          // Delete a message read.
1820          $message = $DB->get_record('messages', ['id' => $m3to2]);
1821          \core_message\api::mark_message_as_read($user3->id, $message, time());
1822          $result = core_message_external::delete_message($m3to2, $user3->id);
1823          $result = \external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1824          $this->assertTrue($result['status']);
1825          $this->assertCount(0, $result['warnings']);
1826          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m3to2, 'userid' => $user3->id,
1827              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
1828  
1829          // Invalid message ids.
1830          try {
1831              $result = core_message_external::delete_message(-1, $user1->id);
1832              $this->fail('Exception expected due invalid messageid.');
1833          } catch (\dml_missing_record_exception $e) {
1834              $this->assertEquals('invalidrecord', $e->errorcode);
1835          }
1836  
1837          // Invalid user.
1838          try {
1839              $result = core_message_external::delete_message($m1to2, -1, false);
1840              $this->fail('Exception expected due invalid user.');
1841          } catch (\moodle_exception $e) {
1842              $this->assertEquals('invaliduser', $e->errorcode);
1843          }
1844  
1845          // Not active user.
1846          delete_user($user2);
1847          try {
1848              $result = core_message_external::delete_message($m1to2, $user2->id, false);
1849              $this->fail('Exception expected due invalid user.');
1850          } catch (\moodle_exception $e) {
1851              $this->assertEquals('userdeleted', $e->errorcode);
1852          }
1853  
1854          // Now, as an admin, try to delete any message.
1855          $this->setAdminUser();
1856          $result = core_message_external::delete_message($m3to4, $user4->id, false);
1857          $result = \external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1858          $this->assertTrue($result['status']);
1859          $this->assertCount(0, $result['warnings']);
1860          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m3to4, 'userid' => $user4->id,
1861              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
1862  
1863      }
1864  
1865      public function test_mark_all_notifications_as_read_invalid_user_exception() {
1866          $this->resetAfterTest(true);
1867  
1868          $this->expectException('moodle_exception');
1869          core_message_external::mark_all_notifications_as_read(-2132131, 0);
1870      }
1871  
1872      public function test_mark_all_notifications_as_read_access_denied_exception() {
1873          $this->resetAfterTest(true);
1874  
1875          $sender = $this->getDataGenerator()->create_user();
1876          $user = $this->getDataGenerator()->create_user();
1877  
1878          $this->setUser($user);
1879          $this->expectException('moodle_exception');
1880          core_message_external::mark_all_notifications_as_read($sender->id, 0);
1881      }
1882  
1883      public function test_mark_all_notifications_as_read_missing_from_user_exception() {
1884          $this->resetAfterTest(true);
1885  
1886          $sender = $this->getDataGenerator()->create_user();
1887  
1888          $this->setUser($sender);
1889          $this->expectException('moodle_exception');
1890          core_message_external::mark_all_notifications_as_read($sender->id, 99999);
1891      }
1892  
1893      public function test_mark_all_notifications_as_read() {
1894          global $DB;
1895  
1896          $this->resetAfterTest(true);
1897  
1898          $sender1 = $this->getDataGenerator()->create_user();
1899          $sender2 = $this->getDataGenerator()->create_user();
1900          $sender3 = $this->getDataGenerator()->create_user();
1901          $recipient = $this->getDataGenerator()->create_user();
1902  
1903          $this->setUser($recipient);
1904  
1905          $this->send_message($sender1, $recipient, 'Notification', 1);
1906          $this->send_message($sender1, $recipient, 'Notification', 1);
1907          $this->send_message($sender2, $recipient, 'Notification', 1);
1908          $this->send_message($sender2, $recipient, 'Notification', 1);
1909          $this->send_message($sender3, $recipient, 'Notification', 1);
1910          $this->send_message($sender3, $recipient, 'Notification', 1);
1911  
1912          core_message_external::mark_all_notifications_as_read($recipient->id, $sender1->id);
1913          $readnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', [$recipient->id]);
1914          $unreadnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NULL', [$recipient->id]);
1915  
1916          $this->assertCount(2, $readnotifications);
1917          $this->assertCount(4, $unreadnotifications);
1918  
1919          core_message_external::mark_all_notifications_as_read($recipient->id, 0);
1920          $readnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', [$recipient->id]);
1921          $unreadnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NULL', [$recipient->id]);
1922  
1923          $this->assertCount(6, $readnotifications);
1924          $this->assertCount(0, $unreadnotifications);
1925      }
1926  
1927      public function test_mark_all_notifications_as_read_time_created_to() {
1928          global $DB;
1929  
1930          $this->resetAfterTest(true);
1931  
1932          $sender1 = $this->getDataGenerator()->create_user();
1933          $sender2 = $this->getDataGenerator()->create_user();
1934  
1935          $recipient = $this->getDataGenerator()->create_user();
1936          $this->setUser($recipient);
1937  
1938          // Record messages as sent on one second intervals.
1939          $time = time();
1940  
1941          $this->send_message($sender1, $recipient, 'Message 1', 1, $time);
1942          $this->send_message($sender2, $recipient, 'Message 2', 1, $time + 1);
1943          $this->send_message($sender1, $recipient, 'Message 3', 1, $time + 2);
1944          $this->send_message($sender2, $recipient, 'Message 4', 1, $time + 3);
1945  
1946          // Mark notifications sent from sender1 up until the second message; should only mark the first notification as read.
1947          core_message_external::mark_all_notifications_as_read($recipient->id, $sender1->id, $time + 1);
1948  
1949          $params = [$recipient->id];
1950  
1951          $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
1952          $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
1953  
1954          // Mark all notifications as read from any sender up to the time the third message was sent.
1955          core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 2);
1956  
1957          $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
1958          $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
1959  
1960          // Mark all notifications as read from any sender with a time after all messages were sent.
1961          core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 10);
1962  
1963          $this->assertEquals(4, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
1964          $this->assertEquals(0, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
1965      }
1966  
1967      /**
1968       * Test get_user_notification_preferences
1969       */
1970      public function test_get_user_notification_preferences() {
1971          $this->resetAfterTest(true);
1972  
1973          $user = self::getDataGenerator()->create_user();
1974          $this->setUser($user);
1975  
1976          // Set a couple of preferences to test.
1977          set_user_preference('message_provider_mod_assign_assign_notification_loggedin', 'popup', $user);
1978          set_user_preference('message_provider_mod_assign_assign_notification_loggedoff', 'email', $user);
1979  
1980          $prefs = core_message_external::get_user_notification_preferences();
1981          $prefs = \external_api::clean_returnvalue(core_message_external::get_user_notification_preferences_returns(), $prefs);
1982          // Check processors.
1983          $this->assertGreaterThanOrEqual(2, count($prefs['preferences']['processors']));
1984          $this->assertEquals($user->id, $prefs['preferences']['userid']);
1985  
1986          // Check components.
1987          $this->assertGreaterThanOrEqual(8, count($prefs['preferences']['components']));
1988  
1989          // Check some preferences that we previously set.
1990          $found = 0;
1991          foreach ($prefs['preferences']['components'] as $component) {
1992              foreach ($component['notifications'] as $prefdata) {
1993                  if ($prefdata['preferencekey'] != 'message_provider_mod_assign_assign_notification') {
1994                      continue;
1995                  }
1996                  foreach ($prefdata['processors'] as $processor) {
1997                      if ($processor['name'] == 'popup') {
1998                          $this->assertTrue($processor['loggedin']['checked']);
1999                          $found++;
2000                      } else if ($processor['name'] == 'email') {
2001                          $this->assertTrue($processor['loggedoff']['checked']);
2002                          $found++;
2003                      }
2004                  }
2005              }
2006          }
2007          $this->assertEquals(2, $found);
2008      }
2009  
2010      /**
2011       * Test get_user_notification_preferences permissions
2012       */
2013      public function test_get_user_notification_preferences_permissions() {
2014          $this->resetAfterTest(true);
2015  
2016          $user = self::getDataGenerator()->create_user();
2017          $otheruser = self::getDataGenerator()->create_user();
2018          $this->setUser($user);
2019  
2020          $this->expectException('moodle_exception');
2021          $prefs = core_message_external::get_user_notification_preferences($otheruser->id);
2022      }
2023  
2024      /**
2025       * Tests searching for users when site-wide messaging is disabled.
2026       *
2027       * This test verifies that any contacts are returned, as well as any non-contacts whose profile we can view.
2028       * If checks this by placing some users in the same course, where default caps would permit a user to view another user's
2029       * profile.
2030       */
2031      public function test_message_search_users_messagingallusers_disabled() {
2032          global $DB;
2033          $this->resetAfterTest();
2034  
2035          // Create some users.
2036          $users = [];
2037          foreach (range(1, 8) as $i) {
2038              $user = new \stdClass();
2039              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2040              $user->lastname = $i;
2041              $user = $this->getDataGenerator()->create_user($user);
2042              $users[$i] = $user;
2043          }
2044  
2045          // Enrol a few users in the same course, but leave them as non-contacts.
2046          $course1 = $this->getDataGenerator()->create_course();
2047          $course2 = $this->getDataGenerator()->create_course();
2048  
2049          $this->setAdminUser();
2050          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2051          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2052          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2053  
2054          // Add some other users as contacts.
2055          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2056          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2057          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2058  
2059          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
2060          $this->getDataGenerator()->enrol_user($users[8]->id, $course2->id, 'editingteacher');
2061          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2062          set_config('coursecontact', $teacherrole->id);
2063  
2064          // Create individual conversations between some users, one contact and one non-contact.
2065          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2066              [$users[1]->id, $users[2]->id]);
2067          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2068              [$users[6]->id, $users[1]->id]);
2069  
2070          // Create a group conversation between 4 users, including a contact and a non-contact.
2071          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2072              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id], 'Project chat');
2073  
2074          // Set as the user performing the search.
2075          $this->setUser($users[1]);
2076  
2077          // Perform a search with $CFG->messagingallusers disabled.
2078          set_config('messagingallusers', 0);
2079          $result = core_message_external::message_search_users($users[1]->id, 'search');
2080          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2081  
2082          // Confirm that we returns contacts and non-contacts.
2083          $this->assertArrayHasKey('contacts', $result);
2084          $this->assertArrayHasKey('noncontacts', $result);
2085          $contacts = $result['contacts'];
2086          $noncontacts = $result['noncontacts'];
2087  
2088          // Check that we retrieved the correct contacts.
2089          $this->assertCount(2, $contacts);
2090          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2091          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2092  
2093          // Verify the correct conversations were returned for the contacts.
2094          $this->assertCount(2, $contacts[0]['conversations']);
2095          // We can't rely on the ordering of conversations within the results, so sort by id first.
2096          usort($contacts[0]['conversations'], function($a, $b) {
2097              return $b['id'] <=> $a['id'];
2098          });
2099          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]['conversations'][0]['type']);
2100          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]['conversations'][1]['type']);
2101  
2102          $this->assertCount(0, $contacts[1]['conversations']);
2103  
2104          // Check that we retrieved the correct non-contacts.
2105          // When site wide messaging is disabled, we expect to see only those users who we share a course with and whose profiles
2106          // are visible in that course. This excludes users like course contacts.
2107          $this->assertCount(3, $noncontacts);
2108          // Self-conversation first.
2109          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2110          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2111          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2112  
2113          // Verify the correct conversations were returned for the non-contacts.
2114          $this->assertCount(1, $noncontacts[1]['conversations']);
2115          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $noncontacts[1]['conversations'][0]['type']);
2116  
2117          $this->assertCount(1, $noncontacts[2]['conversations']);
2118          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]['conversations'][0]['type']);
2119      }
2120  
2121      /**
2122       * Tests searching for users when site-wide messaging is enabled.
2123       *
2124       * This test verifies that any contacts are returned, as well as any non-contacts, regardless of whether the searching user
2125       * can view their respective profile.
2126       */
2127      public function test_message_search_users_messagingallusers_enabled() {
2128          global $DB;
2129          $this->resetAfterTest();
2130  
2131          // Create some users.
2132          $users = [];
2133          foreach (range(1, 9) as $i) {
2134              $user = new \stdClass();
2135              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2136              $user->lastname = $i;
2137              $user = $this->getDataGenerator()->create_user($user);
2138              $users[$i] = $user;
2139          }
2140  
2141          // Enrol a few users in the same course, but leave them as non-contacts.
2142          $course1 = $this->getDataGenerator()->create_course();
2143          $course2 = $this->getDataGenerator()->create_course();
2144  
2145          $this->setAdminUser();
2146          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2147          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2148          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2149  
2150          // Add some other users as contacts.
2151          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2152          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2153          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2154  
2155          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
2156          $this->getDataGenerator()->enrol_user($users[9]->id, $course2->id, 'editingteacher');
2157          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2158          set_config('coursecontact', $teacherrole->id);
2159  
2160          // Create individual conversations between some users, one contact and one non-contact.
2161          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2162              [$users[1]->id, $users[2]->id]);
2163          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2164              [$users[6]->id, $users[1]->id]);
2165  
2166          // Create a group conversation between 5 users, including a contact and a non-contact, and a user NOT in a shared course.
2167          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2168              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id, $users[8]->id], 'Project chat');
2169  
2170          // Set as the user performing the search.
2171          $this->setUser($users[1]);
2172  
2173          // Perform a search with $CFG->messagingallusers enabled.
2174          set_config('messagingallusers', 1);
2175          $result = core_message_external::message_search_users($users[1]->id, 'search');
2176          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2177  
2178          // Confirm that we returns contacts and non-contacts.
2179          $this->assertArrayHasKey('contacts', $result);
2180          $this->assertArrayHasKey('noncontacts', $result);
2181          $contacts = $result['contacts'];
2182          $noncontacts = $result['noncontacts'];
2183  
2184          // Check that we retrieved the correct contacts.
2185          $this->assertCount(2, $contacts);
2186          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2187          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2188  
2189          // Verify the correct conversations were returned for the contacts.
2190          $this->assertCount(2, $contacts[0]['conversations']);
2191          // We can't rely on the ordering of conversations within the results, so sort by id first.
2192          usort($contacts[0]['conversations'], function($a, $b) {
2193              return $b['id'] <=> $a['id'];
2194          });
2195          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]['conversations'][0]['type']);
2196          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]['conversations'][1]['type']);
2197  
2198          $this->assertCount(0, $contacts[1]['conversations']);
2199  
2200          // Check that we retrieved the correct non-contacts.
2201          // If site wide messaging is enabled, we expect to be able to search for any users whose profiles we can view.
2202          // In this case, as a student, that's the course contact for course2 and those noncontacts sharing a course with user1.
2203          $this->assertCount(4, $noncontacts);
2204          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2205          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2206          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2207          $this->assertEquals($users[9]->id, $noncontacts[3]['id']);
2208  
2209          // Verify the correct conversations were returned for the non-contacts.
2210          $this->assertCount(1, $noncontacts[1]['conversations']);
2211          $this->assertCount(1, $noncontacts[2]['conversations']);
2212          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $noncontacts[1]['conversations'][0]['type']);
2213          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]['conversations'][0]['type']);
2214          $this->assertCount(0, $noncontacts[3]['conversations']);
2215      }
2216  
2217      /**
2218       * Verify searching for users find themselves when they have self-conversations.
2219       */
2220      public function test_message_search_users_self_conversations() {
2221          $this->resetAfterTest();
2222  
2223          // Create some users.
2224          $user1 = new \stdClass();
2225          $user1->firstname = 'User';
2226          $user1->lastname = 'One';
2227          $user1 = $this->getDataGenerator()->create_user($user1);
2228          $user2 = new \stdClass();
2229          $user2->firstname = 'User';
2230          $user2->lastname = 'Two';
2231          $user2 = $this->getDataGenerator()->create_user($user2);
2232  
2233          // Get self-conversation for user1.
2234          $sc1 = \core_message\api::get_self_conversation($user1->id);
2235          testhelper::send_fake_message_to_conversation($user1, $sc1->id, 'Hi myself!');
2236  
2237          // Perform a search as user1.
2238          $this->setUser($user1);
2239          $result = core_message_external::message_search_users($user1->id, 'One');
2240          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2241  
2242          // Check results are empty.
2243          $this->assertCount(0, $result['contacts']);
2244          $this->assertCount(1, $result['noncontacts']);
2245      }
2246  
2247      /**
2248       * Verify searching for users works even if no matching users from either contacts, or non-contacts can be found.
2249       */
2250      public function test_message_search_users_with_empty_result() {
2251          $this->resetAfterTest();
2252  
2253          // Create some users, but make sure neither will match the search term.
2254          $user1 = new \stdClass();
2255          $user1->firstname = 'User';
2256          $user1->lastname = 'One';
2257          $user1 = $this->getDataGenerator()->create_user($user1);
2258          $user2 = new \stdClass();
2259          $user2->firstname = 'User';
2260          $user2->lastname = 'Two';
2261          $user2 = $this->getDataGenerator()->create_user($user2);
2262  
2263          // Perform a search as user1.
2264          $this->setUser($user1);
2265          $result = core_message_external::message_search_users($user1->id, 'search');
2266          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2267  
2268          // Check results are empty.
2269          $this->assertCount(0, $result['contacts']);
2270          $this->assertCount(0, $result['noncontacts']);
2271      }
2272  
2273      /**
2274       * Test verifying that limits and offsets work for both the contacts and non-contacts return data.
2275       */
2276      public function test_message_search_users_limit_offset() {
2277          $this->resetAfterTest();
2278  
2279          // Create 20 users.
2280          $users = [];
2281          foreach (range(1, 20) as $i) {
2282              $user = new \stdClass();
2283              $user->firstname = "User search";
2284              $user->lastname = $i;
2285              $user = $this->getDataGenerator()->create_user($user);
2286              $users[$i] = $user;
2287          }
2288  
2289          // Enrol the first 8 users in the same course, but leave them as non-contacts.
2290          $this->setAdminUser();
2291          $course1 = $this->getDataGenerator()->create_course();
2292          foreach (range(1, 8) as $i) {
2293              $this->getDataGenerator()->enrol_user($users[$i]->id, $course1->id);
2294          }
2295  
2296          // Add 5 users, starting at the 11th user, as contacts for user1.
2297          foreach (range(11, 15) as $i) {
2298              \core_message\api::add_contact($users[1]->id, $users[$i]->id);
2299          }
2300  
2301          // Set as the user performing the search.
2302          $this->setUser($users[1]);
2303  
2304          // Search using a limit of 3.
2305          // This tests the case where we have more results than the limit for both contacts and non-contacts.
2306          $result = core_message_external::message_search_users($users[1]->id, 'search', 0, 3);
2307          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2308          $contacts = $result['contacts'];
2309          $noncontacts = $result['noncontacts'];
2310  
2311          // Check that we retrieved the correct contacts.
2312          $this->assertCount(3, $contacts);
2313          $this->assertEquals($users[11]->id, $contacts[0]['id']);
2314          $this->assertEquals($users[12]->id, $contacts[1]['id']);
2315          $this->assertEquals($users[13]->id, $contacts[2]['id']);
2316  
2317          // Check that we retrieved the correct non-contacts.
2318          // Consider first conversation is self-conversation.
2319          $this->assertCount(3, $noncontacts);
2320          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2321          $this->assertEquals($users[2]->id, $noncontacts[1]['id']);
2322          $this->assertEquals($users[3]->id, $noncontacts[2]['id']);
2323  
2324          // Now, offset to get the next batch of results.
2325          // We expect to see 2 contacts, and 3 non-contacts.
2326          $result = core_message_external::message_search_users($users[1]->id, 'search', 3, 3);
2327          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2328          $contacts = $result['contacts'];
2329          $noncontacts = $result['noncontacts'];
2330          $this->assertCount(2, $contacts);
2331          $this->assertEquals($users[14]->id, $contacts[0]['id']);
2332          $this->assertEquals($users[15]->id, $contacts[1]['id']);
2333  
2334          $this->assertCount(3, $noncontacts);
2335          $this->assertEquals($users[4]->id, $noncontacts[0]['id']);
2336          $this->assertEquals($users[5]->id, $noncontacts[1]['id']);
2337          $this->assertEquals($users[6]->id, $noncontacts[2]['id']);
2338  
2339          // Now, offset to get the next batch of results.
2340          // We expect to see 0 contacts, and 2 non-contacts.
2341          $result = core_message_external::message_search_users($users[1]->id, 'search', 6, 3);
2342          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2343          $contacts = $result['contacts'];
2344          $noncontacts = $result['noncontacts'];
2345          $this->assertCount(0, $contacts);
2346  
2347          $this->assertCount(2, $noncontacts);
2348          $this->assertEquals($users[7]->id, $noncontacts[0]['id']);
2349          $this->assertEquals($users[8]->id, $noncontacts[1]['id']);
2350      }
2351  
2352      /**
2353       * Tests searching users as another user having the 'moodle/user:viewdetails' capability.
2354       */
2355      public function test_message_search_users_with_cap() {
2356          $this->resetAfterTest();
2357          global $DB;
2358  
2359          // Create some users.
2360          $users = [];
2361          foreach (range(1, 8) as $i) {
2362              $user = new \stdClass();
2363              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2364              $user->lastname = $i;
2365              $user = $this->getDataGenerator()->create_user($user);
2366              $users[$i] = $user;
2367          }
2368  
2369          // Enrol a few users in the same course, but leave them as non-contacts.
2370          $course1 = $this->getDataGenerator()->create_course();
2371          $this->setAdminUser();
2372          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2373          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2374          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2375  
2376          // Add some other users as contacts.
2377          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2378          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2379          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2380  
2381          // Set as the user performing the search.
2382          $this->setUser($users[1]);
2383  
2384          // Grant the authenticated user role the capability 'user:viewdetails' at site context.
2385          $authenticatedrole = $DB->get_record('role', ['shortname' => 'user'], '*', MUST_EXIST);
2386          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $authenticatedrole->id, \context_system::instance());
2387  
2388          // Perform a search with $CFG->messagingallusers disabled.
2389          set_config('messagingallusers', 0);
2390          $result = core_message_external::message_search_users($users[1]->id, 'search');
2391          $result = \external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2392          $contacts = $result['contacts'];
2393          $noncontacts = $result['noncontacts'];
2394  
2395          // Check that we retrieved the correct contacts.
2396          $this->assertCount(2, $contacts);
2397          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2398          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2399  
2400          // Check that we retrieved the correct non-contacts.
2401          // Site-wide messaging is disabled, so we expect to be able to search for any users whose profile we can view.
2402          // Consider first conversations is self-conversation.
2403          $this->assertCount(3, $noncontacts);
2404          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2405          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2406          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2407      }
2408  
2409      /**
2410       * Tests searching users as another user without the 'moodle/user:viewdetails' capability.
2411       */
2412      public function test_message_search_users_without_cap() {
2413          $this->resetAfterTest();
2414  
2415          // Create some users.
2416          $user1 = $this->getDataGenerator()->create_user();
2417          $user2 = $this->getDataGenerator()->create_user();
2418  
2419          // The person doing the search for another user.
2420          $this->setUser($user1);
2421  
2422          // Ensure an exception is thrown.
2423          $this->expectException('moodle_exception');
2424          core_message_external::message_search_users($user2->id, 'User');
2425          $this->assertDebuggingCalled();
2426      }
2427  
2428      /**
2429       * Tests searching users with messaging disabled.
2430       */
2431      public function test_message_search_users_messaging_disabled() {
2432          $this->resetAfterTest();
2433  
2434          // Create some skeleton data just so we can call the WS.
2435          $user = $this->getDataGenerator()->create_user();
2436  
2437          // The person doing the search.
2438          $this->setUser($user);
2439  
2440          // Disable messaging.
2441          set_config('messaging', 0);
2442  
2443          // Ensure an exception is thrown.
2444          $this->expectException('moodle_exception');
2445          core_message_external::message_search_users($user->id, 'User');
2446      }
2447  
2448      /**
2449       * Tests searching messages.
2450       */
2451      public function test_messagearea_search_messages() {
2452          $this->resetAfterTest(true);
2453  
2454          // Create some users.
2455          $user1 = self::getDataGenerator()->create_user();
2456          $user2 = self::getDataGenerator()->create_user();
2457  
2458          // The person doing the search.
2459          $this->setUser($user1);
2460  
2461          // Send some messages back and forth.
2462          $time = time();
2463          $this->send_message($user1, $user2, 'Yo!', 0, $time);
2464          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
2465          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
2466          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
2467          $convid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
2468  
2469          // Perform a search.
2470          $result = core_message_external::data_for_messagearea_search_messages($user1->id, 'o');
2471  
2472          // We need to execute the return values cleaning process to simulate the web service server.
2473          $result = \external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_messages_returns(), $result);
2474  
2475          // Confirm the data is correct.
2476          $messages = $result['contacts'];
2477          $this->assertCount(2, $messages);
2478  
2479          $message1 = $messages[0];
2480          $message2 = $messages[1];
2481  
2482          $this->assertEquals($user2->id, $message1['userid']);
2483          $this->assertEquals(fullname($user2), $message1['fullname']);
2484          $this->assertTrue($message1['ismessaging']);
2485          $this->assertFalse($message1['sentfromcurrentuser']);
2486          $this->assertEquals('Word.', $message1['lastmessage']);
2487          $this->assertNotEmpty($message1['messageid']);
2488          $this->assertNull($message1['isonline']);
2489          $this->assertFalse($message1['isread']);
2490          $this->assertFalse($message1['isblocked']);
2491          $this->assertNull($message1['unreadcount']);
2492          $this->assertEquals($convid, $message1['conversationid']);
2493  
2494          $this->assertEquals($user2->id, $message2['userid']);
2495          $this->assertEquals(fullname($user2), $message2['fullname']);
2496          $this->assertTrue($message2['ismessaging']);
2497          $this->assertTrue($message2['sentfromcurrentuser']);
2498          $this->assertEquals('Yo!', $message2['lastmessage']);
2499          $this->assertNotEmpty($message2['messageid']);
2500          $this->assertNull($message2['isonline']);
2501          $this->assertTrue($message2['isread']);
2502          $this->assertFalse($message2['isblocked']);
2503          $this->assertNull($message2['unreadcount']);
2504          $this->assertEquals($convid, $message2['conversationid']);
2505      }
2506  
2507      /**
2508       * Tests searching messages as another user.
2509       */
2510      public function test_messagearea_search_messages_as_other_user() {
2511          $this->resetAfterTest(true);
2512  
2513          // The person doing the search.
2514          $this->setAdminUser();
2515  
2516          // Create some users.
2517          $user1 = self::getDataGenerator()->create_user();
2518          $user2 = self::getDataGenerator()->create_user();
2519  
2520          // Send some messages back and forth.
2521          $time = time();
2522          $this->send_message($user1, $user2, 'Yo!', 0, $time);
2523          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
2524          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
2525          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
2526  
2527          // Perform a search.
2528          $result = core_message_external::data_for_messagearea_search_messages($user1->id, 'o');
2529  
2530          // We need to execute the return values cleaning process to simulate the web service server.
2531          $result = \external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_messages_returns(),
2532              $result);
2533  
2534          // Confirm the data is correct.
2535          $messages = $result['contacts'];
2536          $this->assertCount(2, $messages);
2537  
2538          $message1 = $messages[0];
2539          $message2 = $messages[1];
2540  
2541          $this->assertEquals($user2->id, $message1['userid']);
2542          $this->assertEquals(fullname($user2), $message1['fullname']);
2543          $this->assertTrue($message1['ismessaging']);
2544          $this->assertFalse($message1['sentfromcurrentuser']);
2545          $this->assertEquals('Word.', $message1['lastmessage']);
2546          $this->assertNotEmpty($message1['messageid']);
2547          $this->assertFalse($message1['isonline']);
2548          $this->assertFalse($message1['isread']);
2549          $this->assertFalse($message1['isblocked']);
2550          $this->assertNull($message1['unreadcount']);
2551  
2552          $this->assertEquals($user2->id, $message2['userid']);
2553          $this->assertEquals(fullname($user2), $message2['fullname']);
2554          $this->assertTrue($message2['ismessaging']);
2555          $this->assertTrue($message2['sentfromcurrentuser']);
2556          $this->assertEquals('Yo!', $message2['lastmessage']);
2557          $this->assertNotEmpty($message2['messageid']);
2558          $this->assertFalse($message2['isonline']);
2559          $this->assertTrue($message2['isread']);
2560          $this->assertFalse($message2['isblocked']);
2561          $this->assertNull($message2['unreadcount']);
2562      }
2563  
2564      /**
2565       * Tests searching messages as another user without the proper capabilities.
2566       */
2567      public function test_messagearea_search_messages_as_other_user_without_cap() {
2568          $this->resetAfterTest(true);
2569  
2570          // Create some users.
2571          $user1 = self::getDataGenerator()->create_user();
2572          $user2 = self::getDataGenerator()->create_user();
2573  
2574          // The person doing the search for another user.
2575          $this->setUser($user1);
2576  
2577          // Ensure an exception is thrown.
2578          $this->expectException('moodle_exception');
2579          core_message_external::data_for_messagearea_search_messages($user2->id, 'Search');
2580      }
2581  
2582      /**
2583       * Tests searching messages with messaging disabled
2584       */
2585      public function test_messagearea_search_messages_messaging_disabled() {
2586          global $CFG;
2587  
2588          $this->resetAfterTest(true);
2589  
2590          // Create some skeleton data just so we can call the WS.
2591          $user = self::getDataGenerator()->create_user();
2592  
2593          // The person doing the search .
2594          $this->setUser($user);
2595  
2596          // Disable messaging.
2597          $CFG->messaging = 0;
2598  
2599          // Ensure an exception is thrown.
2600          $this->expectException('moodle_exception');
2601          core_message_external::data_for_messagearea_search_messages($user->id, 'Search');
2602      }
2603  
2604      /**
2605       * Tests retrieving contacts.
2606       */
2607      public function test_get_user_contacts() {
2608          $this->resetAfterTest(true);
2609  
2610          // Create some users.
2611          $user1 = self::getDataGenerator()->create_user();
2612  
2613          // Set as the user.
2614          $this->setUser($user1);
2615  
2616          $user2 = new \stdClass();
2617          $user2->firstname = 'User';
2618          $user2->lastname = 'A';
2619          $user2 = self::getDataGenerator()->create_user($user2);
2620  
2621          $user3 = new \stdClass();
2622          $user3->firstname = 'User';
2623          $user3->lastname = 'B';
2624          $user3 = self::getDataGenerator()->create_user($user3);
2625  
2626          $user4 = new \stdClass();
2627          $user4->firstname = 'User';
2628          $user4->lastname = 'C';
2629          $user4 = self::getDataGenerator()->create_user($user4);
2630  
2631          $user5 = new \stdClass();
2632          $user5->firstname = 'User';
2633          $user5->lastname = 'D';
2634          $user5 = self::getDataGenerator()->create_user($user5);
2635  
2636          // Add some users as contacts.
2637          \core_message\api::add_contact($user1->id, $user2->id);
2638          \core_message\api::add_contact($user1->id, $user3->id);
2639          \core_message\api::add_contact($user1->id, $user4->id);
2640  
2641          // Retrieve the contacts.
2642          $result = core_message_external::get_user_contacts($user1->id);
2643  
2644          // We need to execute the return values cleaning process to simulate the web service server.
2645          $result = \external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(),
2646              $result);
2647  
2648          // Confirm the data is correct.
2649          $contacts = $result;
2650          usort($contacts, ['static', 'sort_contacts_id']);
2651          $this->assertCount(3, $contacts);
2652  
2653          $contact1 = array_shift($contacts);
2654          $contact2 = array_shift($contacts);
2655          $contact3 = array_shift($contacts);
2656  
2657          $this->assertEquals($user2->id, $contact1['id']);
2658          $this->assertEquals(fullname($user2), $contact1['fullname']);
2659          $this->assertTrue($contact1['iscontact']);
2660  
2661          $this->assertEquals($user3->id, $contact2['id']);
2662          $this->assertEquals(fullname($user3), $contact2['fullname']);
2663          $this->assertTrue($contact2['iscontact']);
2664  
2665          $this->assertEquals($user4->id, $contact3['id']);
2666          $this->assertEquals(fullname($user4), $contact3['fullname']);
2667          $this->assertTrue($contact3['iscontact']);
2668      }
2669  
2670      /**
2671       * Tests retrieving contacts as another user.
2672       */
2673      public function test_get_user_contacts_as_other_user() {
2674          $this->resetAfterTest(true);
2675  
2676          $this->setAdminUser();
2677  
2678          // Create some users.
2679          $user1 = self::getDataGenerator()->create_user();
2680  
2681          $user2 = new \stdClass();
2682          $user2->firstname = 'User';
2683          $user2->lastname = 'A';
2684          $user2 = self::getDataGenerator()->create_user($user2);
2685  
2686          $user3 = new \stdClass();
2687          $user3->firstname = 'User';
2688          $user3->lastname = 'B';
2689          $user3 = self::getDataGenerator()->create_user($user3);
2690  
2691          $user4 = new \stdClass();
2692          $user4->firstname = 'User';
2693          $user4->lastname = 'C';
2694          $user4 = self::getDataGenerator()->create_user($user4);
2695  
2696          $user5 = new \stdClass();
2697          $user5->firstname = 'User';
2698          $user5->lastname = 'D';
2699          $user5 = self::getDataGenerator()->create_user($user5);
2700  
2701          // Add some users as contacts.
2702          \core_message\api::add_contact($user1->id, $user2->id);
2703          \core_message\api::add_contact($user1->id, $user3->id);
2704          \core_message\api::add_contact($user1->id, $user4->id);
2705  
2706          // Retrieve the contacts.
2707          $result = core_message_external::get_user_contacts($user1->id);
2708  
2709          // We need to execute the return values cleaning process to simulate the web service server.
2710          $result = \external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(),
2711              $result);
2712  
2713          // Confirm the data is correct.
2714          $contacts = $result;
2715          usort($contacts, ['static', 'sort_contacts_id']);
2716          $this->assertCount(3, $contacts);
2717  
2718          $contact1 = array_shift($contacts);
2719          $contact2 = array_shift($contacts);
2720          $contact3 = array_shift($contacts);
2721  
2722          $this->assertEquals($user2->id, $contact1['id']);
2723          $this->assertEquals(fullname($user2), $contact1['fullname']);
2724          $this->assertTrue($contact1['iscontact']);
2725  
2726          $this->assertEquals($user3->id, $contact2['id']);
2727          $this->assertEquals(fullname($user3), $contact2['fullname']);
2728          $this->assertTrue($contact2['iscontact']);
2729  
2730          $this->assertEquals($user4->id, $contact3['id']);
2731          $this->assertEquals(fullname($user4), $contact3['fullname']);
2732          $this->assertTrue($contact3['iscontact']);
2733      }
2734  
2735      /**
2736       * Tests retrieving contacts as another user without the proper capabilities.
2737       */
2738      public function test_get_user_contacts_as_other_user_without_cap() {
2739          $this->resetAfterTest(true);
2740  
2741          // Create some users.
2742          $user1 = self::getDataGenerator()->create_user();
2743          $user2 = self::getDataGenerator()->create_user();
2744  
2745          // The person retrieving the contacts for another user.
2746          $this->setUser($user1);
2747  
2748          // Perform the WS call and ensure an exception is thrown.
2749          $this->expectException('moodle_exception');
2750          core_message_external::get_user_contacts($user2->id);
2751      }
2752  
2753      /**
2754       * Tests retrieving contacts with messaging disabled.
2755       */
2756      public function test_get_user_contacts_messaging_disabled() {
2757          global $CFG;
2758  
2759          $this->resetAfterTest(true);
2760  
2761          // Create some skeleton data just so we can call the WS.
2762          $user = self::getDataGenerator()->create_user();
2763  
2764          // The person retrieving the contacts.
2765          $this->setUser($user);
2766  
2767          // Disable messaging.
2768          $CFG->messaging = 0;
2769  
2770          // Perform the WS call and ensure we are shown that it is disabled.
2771          $this->expectException('moodle_exception');
2772          core_message_external::get_user_contacts($user->id);
2773      }
2774  
2775      /**
2776       * Test getting contacts when there are no results.
2777       */
2778      public function test_get_user_contacts_no_results() {
2779          $this->resetAfterTest();
2780  
2781          $user1 = self::getDataGenerator()->create_user();
2782  
2783          $this->setUser($user1);
2784  
2785          $requests = core_message_external::get_user_contacts($user1->id);
2786          $requests = \external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(), $requests);
2787  
2788          $this->assertEmpty($requests);
2789      }
2790  
2791      /**
2792       * Tests get_conversation_messages for retrieving messages.
2793       */
2794      public function test_get_conversation_messages() {
2795          $this->resetAfterTest(true);
2796  
2797          // Create some users.
2798          $user1 = self::getDataGenerator()->create_user();
2799          $user2 = self::getDataGenerator()->create_user();
2800          $user3 = self::getDataGenerator()->create_user();
2801          $user4 = self::getDataGenerator()->create_user();
2802          $user5 = self::getDataGenerator()->create_user();
2803  
2804          // Create group conversation.
2805          $conversation = \core_message\api::create_conversation(
2806              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2807              [$user1->id, $user2->id, $user3->id, $user4->id]
2808          );
2809  
2810          // The person asking for the messages.
2811          $this->setUser($user1);
2812  
2813          // Send some messages back and forth.
2814          $time = time();
2815          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time);
2816          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Sup mang?', $time + 1);
2817          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Writing PHPUnit tests!', $time + 2);
2818          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 3);
2819  
2820          // Retrieve the messages.
2821          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id);
2822  
2823          // We need to execute the return values cleaning process to simulate the web service server.
2824          $result = \external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
2825              $result);
2826  
2827          // Check the results are correct.
2828          $this->assertEquals($conversation->id, $result['id']);
2829  
2830          // Confirm the members data is correct.
2831          $members = $result['members'];
2832          $this->assertCount(3, $members);
2833          $membersid = [$members[0]['id'], $members[1]['id'], $members[2]['id']];
2834          $this->assertContainsEquals($user1->id, $membersid);
2835          $this->assertContainsEquals($user2->id, $membersid);
2836          $this->assertContainsEquals($user3->id, $membersid);
2837  
2838          $membersfullnames = [$members[0]['fullname'], $members[1]['fullname'], $members[2]['fullname']];
2839          $this->assertContainsEquals(fullname($user1), $membersfullnames);
2840          $this->assertContainsEquals(fullname($user2), $membersfullnames);
2841          $this->assertContainsEquals(fullname($user3), $membersfullnames);
2842  
2843          // Confirm the messages data is correct.
2844          $messages = $result['messages'];
2845          $this->assertCount(4, $messages);
2846  
2847          $message1 = $messages[0];
2848          $message2 = $messages[1];
2849          $message3 = $messages[2];
2850          $message4 = $messages[3];
2851  
2852          $this->assertEquals($user1->id, $message1['useridfrom']);
2853          $this->assertStringContainsString('Yo!', $message1['text']);
2854  
2855          $this->assertEquals($user3->id, $message2['useridfrom']);
2856          $this->assertStringContainsString('Sup mang?', $message2['text']);
2857  
2858          $this->assertEquals($user2->id, $message3['useridfrom']);
2859          $this->assertStringContainsString('Writing PHPUnit tests!', $message3['text']);
2860  
2861          $this->assertEquals($user1->id, $message4['useridfrom']);
2862          $this->assertStringContainsString('Word.', $message4['text']);
2863      }
2864  
2865      /**
2866       * Tests get_conversation_messages for retrieving messages using timefrom parameter.
2867       */
2868      public function test_get_conversation_messages_timefrom() {
2869          $this->resetAfterTest(true);
2870  
2871          // Create some users.
2872          $user1 = self::getDataGenerator()->create_user();
2873          $user2 = self::getDataGenerator()->create_user();
2874          $user3 = self::getDataGenerator()->create_user();
2875          $user4 = self::getDataGenerator()->create_user();
2876  
2877          // Create group conversation.
2878          $conversation = \core_message\api::create_conversation(
2879              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2880              [$user1->id, $user2->id, $user3->id]
2881          );
2882  
2883          // The person asking for the messages.
2884          $this->setUser($user1);
2885  
2886          // Send some messages back and forth.
2887          $time = time();
2888          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time - 4);
2889          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time - 3);
2890          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 3', $time - 2);
2891          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 4', $time - 1);
2892  
2893          // Retrieve the messages from $time - 3, which should be the 3 most recent messages.
2894          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id, 0, 0, false, $time - 3);
2895  
2896          // We need to execute the return values cleaning process to simulate the web service server.
2897          $result = \external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
2898              $result);
2899  
2900          // Check the results are correct.
2901          $this->assertEquals($conversation->id, $result['id']);
2902  
2903          // Confirm the messages data is correct.
2904          $messages = $result['messages'];
2905          $this->assertCount(3, $messages);
2906  
2907          $message1 = $messages[0];
2908          $message2 = $messages[1];
2909          $message3 = $messages[2];
2910  
2911          $this->assertStringContainsString('Message 2', $message1['text']);
2912          $this->assertStringContainsString('Message 3', $message2['text']);
2913          $this->assertStringContainsString('Message 4', $message3['text']);
2914  
2915          // Confirm the members data is correct.
2916          $members = $result['members'];