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 311 and 402] [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  
  21  defined('MOODLE_INTERNAL') || die();
  22  
  23  global $CFG;
  24  
  25  require_once($CFG->dirroot . '/message/tests/messagelib_test.php');
  26  
  27  /**
  28   * Test message API.
  29   *
  30   * @package core_message
  31   * @category test
  32   * @copyright 2016 Mark Nelson <markn@moodle.com>
  33   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class api_test extends messagelib_test {
  36  
  37      public function test_mark_all_read_for_user_touser() {
  38          $sender = $this->getDataGenerator()->create_user(array('firstname' => 'Test1', 'lastname' => 'User1'));
  39          $recipient = $this->getDataGenerator()->create_user(array('firstname' => 'Test2', 'lastname' => 'User2'));
  40  
  41          $this->send_fake_message($sender, $recipient, 'Notification', 1);
  42          $this->send_fake_message($sender, $recipient, 'Notification', 1);
  43          $this->send_fake_message($sender, $recipient, 'Notification', 1);
  44          $this->send_fake_message($sender, $recipient);
  45          $this->send_fake_message($sender, $recipient);
  46          $this->send_fake_message($sender, $recipient);
  47  
  48          api::mark_all_notifications_as_read($recipient->id);
  49          api::mark_all_messages_as_read($recipient->id);
  50  
  51          $this->assertEquals(message_count_unread_messages($recipient), 0);
  52          $this->assertDebuggingCalled();
  53      }
  54  
  55      public function test_mark_all_read_for_user_touser_with_fromuser() {
  56          $sender1 = $this->getDataGenerator()->create_user(array('firstname' => 'Test1', 'lastname' => 'User1'));
  57          $sender2 = $this->getDataGenerator()->create_user(array('firstname' => 'Test3', 'lastname' => 'User3'));
  58          $recipient = $this->getDataGenerator()->create_user(array('firstname' => 'Test2', 'lastname' => 'User2'));
  59  
  60          $this->send_fake_message($sender1, $recipient, 'Notification', 1);
  61          $this->send_fake_message($sender1, $recipient, 'Notification', 1);
  62          $this->send_fake_message($sender1, $recipient, 'Notification', 1);
  63          $this->send_fake_message($sender1, $recipient);
  64          $this->send_fake_message($sender1, $recipient);
  65          $this->send_fake_message($sender1, $recipient);
  66          $this->send_fake_message($sender2, $recipient, 'Notification', 1);
  67          $this->send_fake_message($sender2, $recipient, 'Notification', 1);
  68          $this->send_fake_message($sender2, $recipient, 'Notification', 1);
  69          $this->send_fake_message($sender2, $recipient);
  70          $this->send_fake_message($sender2, $recipient);
  71          $this->send_fake_message($sender2, $recipient);
  72  
  73          api::mark_all_notifications_as_read($recipient->id, $sender1->id);
  74          $conversationid = api::get_conversation_between_users([$recipient->id, $sender1->id]);
  75          api::mark_all_messages_as_read($recipient->id, $conversationid);
  76  
  77          $this->assertEquals(message_count_unread_messages($recipient), 3);
  78          $this->assertDebuggingCalled();
  79      }
  80  
  81      public function test_mark_all_read_for_user_touser_with_type() {
  82          $sender = $this->getDataGenerator()->create_user(array('firstname' => 'Test1', 'lastname' => 'User1'));
  83          $recipient = $this->getDataGenerator()->create_user(array('firstname' => 'Test2', 'lastname' => 'User2'));
  84  
  85          $this->send_fake_message($sender, $recipient, 'Notification', 1);
  86          $this->send_fake_message($sender, $recipient, 'Notification', 1);
  87          $this->send_fake_message($sender, $recipient, 'Notification', 1);
  88          $this->send_fake_message($sender, $recipient);
  89          $this->send_fake_message($sender, $recipient);
  90          $this->send_fake_message($sender, $recipient);
  91  
  92          api::mark_all_notifications_as_read($recipient->id);
  93          $this->assertEquals(message_count_unread_messages($recipient), 3);
  94          $this->assertDebuggingCalled();
  95  
  96          api::mark_all_messages_as_read($recipient->id);
  97          $this->assertEquals(message_count_unread_messages($recipient), 0);
  98          $this->assertDebuggingCalled();
  99      }
 100  
 101      /**
 102       * Test count_blocked_users.
 103       */
 104      public function test_count_blocked_users() {
 105          global $USER;
 106  
 107          // Set this user as the admin.
 108          $this->setAdminUser();
 109  
 110          // Create user to add to the admin's block list.
 111          $user1 = $this->getDataGenerator()->create_user();
 112          $user2 = $this->getDataGenerator()->create_user();
 113  
 114          $this->assertEquals(0, api::count_blocked_users());
 115  
 116          // Add 1 blocked user to admin's blocked user list.
 117          api::block_user($USER->id, $user1->id);
 118  
 119          $this->assertEquals(0, api::count_blocked_users($user1));
 120          $this->assertEquals(1, api::count_blocked_users());
 121      }
 122  
 123      /**
 124       * Tests searching for users when site-wide messaging is disabled.
 125       *
 126       * This test verifies that any contacts are returned, as well as any non-contacts whose profile we can view.
 127       * If checks this by placing some users in the same course, where default caps would permit a user to view another user's
 128       * profile.
 129       */
 130      public function test_message_search_users_messagingallusers_disabled() {
 131          global $DB;
 132          $this->resetAfterTest();
 133  
 134          // Create some users.
 135          $users = [];
 136          foreach (range(1, 8) as $i) {
 137              $user = new \stdClass();
 138              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
 139              $user->lastname = $i;
 140              $user = $this->getDataGenerator()->create_user($user);
 141              $users[$i] = $user;
 142          }
 143  
 144          // Enrol a few users in the same course, but leave them as non-contacts.
 145          $course1 = $this->getDataGenerator()->create_course();
 146          $course2 = $this->getDataGenerator()->create_course();
 147  
 148          $this->setAdminUser();
 149          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
 150          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
 151          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
 152  
 153          // Add some other users as contacts.
 154          api::add_contact($users[1]->id, $users[2]->id);
 155          api::add_contact($users[3]->id, $users[1]->id);
 156          api::add_contact($users[1]->id, $users[4]->id);
 157  
 158          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
 159          $this->getDataGenerator()->enrol_user($users[8]->id, $course2->id, 'editingteacher');
 160          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 161          set_config('coursecontact', $teacherrole->id);
 162  
 163          // Create individual conversations between some users, one contact and one non-contact.
 164          $ic1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 165              [$users[1]->id, $users[2]->id]);
 166          $ic2 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 167              [$users[6]->id, $users[1]->id]);
 168  
 169          // Create a group conversation between 4 users, including a contact and a non-contact.
 170          $gc1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
 171              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id], 'Project chat');
 172  
 173          // Set as the user performing the search.
 174          $this->setUser($users[1]);
 175  
 176          // Perform a search with $CFG->messagingallusers disabled.
 177          set_config('messagingallusers', 0);
 178          $result = api::message_search_users($users[1]->id, 'search');
 179  
 180          // Confirm that we returns contacts and non-contacts.
 181          $this->assertArrayHasKey(0, $result);
 182          $this->assertArrayHasKey(1, $result);
 183          $contacts = $result[0];
 184          $noncontacts = $result[1];
 185  
 186          // Check that we retrieved the correct contacts.
 187          $this->assertCount(2, $contacts);
 188          $this->assertEquals($users[2]->id, $contacts[0]->id);
 189          $this->assertEquals($users[3]->id, $contacts[1]->id);
 190  
 191          // Verify the correct conversations were returned for the contacts.
 192          $this->assertCount(2, $contacts[0]->conversations);
 193          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]->conversations[$gc1->id]->type);
 194          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]->conversations[$ic1->id]->type);
 195  
 196          $this->assertCount(0, $contacts[1]->conversations);
 197  
 198          // Check that we retrieved the correct non-contacts.
 199          // When site wide messaging is disabled, we expect to see only those users who we share a course with and whose profiles
 200          // are visible in that course. This excludes users like course contacts.
 201          $this->assertCount(3, $noncontacts);
 202          // Self-conversation first.
 203          $this->assertEquals($users[1]->id, $noncontacts[0]->id);
 204          $this->assertEquals($users[6]->id, $noncontacts[1]->id);
 205          $this->assertEquals($users[7]->id, $noncontacts[2]->id);
 206  
 207          // Verify the correct conversations were returned for the non-contacts.
 208          $this->assertCount(1, $noncontacts[1]->conversations);
 209          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 210              $noncontacts[1]->conversations[$ic2->id]->type);
 211  
 212          $this->assertCount(1, $noncontacts[2]->conversations);
 213          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]->conversations[$gc1->id]->type);
 214      }
 215  
 216      /**
 217       * Tests searching for users when site-wide messaging is enabled.
 218       *
 219       * This test verifies that any contacts are returned, as well as any non-contacts,
 220       * provided the searching user can view their profile.
 221       */
 222      public function test_message_search_users_messagingallusers_enabled() {
 223          global $DB;
 224          $this->resetAfterTest();
 225  
 226          // Create some users.
 227          $users = [];
 228          foreach (range(1, 9) as $i) {
 229              $user = new \stdClass();
 230              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
 231              $user->lastname = $i;
 232              $user = $this->getDataGenerator()->create_user($user);
 233              $users[$i] = $user;
 234          }
 235  
 236          $course1 = $this->getDataGenerator()->create_course();
 237          $coursecontext = \context_course::instance($course1->id);
 238  
 239          // Enrol a few users in the same course, but leave them as non-contacts.
 240          $this->setAdminUser();
 241          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id, 'student');
 242          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id, 'student');
 243          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id, 'student');
 244  
 245          // Add some other users as contacts.
 246          api::add_contact($users[1]->id, $users[2]->id);
 247          api::add_contact($users[3]->id, $users[1]->id);
 248          api::add_contact($users[1]->id, $users[4]->id);
 249  
 250          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
 251          $this->getDataGenerator()->enrol_user($users[9]->id, $course1->id, 'editingteacher');
 252          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 253          set_config('coursecontact', $teacherrole->id);
 254  
 255          // Get self-conversation.
 256          $selfconversation = api::get_self_conversation($users[1]->id);
 257  
 258          // Create individual conversations between some users, one contact and one non-contact.
 259          $ic1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 260              [$users[1]->id, $users[2]->id]);
 261          $ic2 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 262              [$users[6]->id, $users[1]->id]);
 263  
 264          // Create a group conversation between 5 users, including a contact and a non-contact, and a user NOT in a shared course.
 265          $gc1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
 266              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id, $users[8]->id], 'Project chat');
 267  
 268          // Set as the user performing the search.
 269          $this->setUser($users[1]);
 270  
 271          // Perform a search with $CFG->messagingallusers enabled.
 272          set_config('messagingallusers', 1);
 273          $result = api::message_search_users($users[1]->id, 'search');
 274  
 275          // Confirm that we returns contacts and non-contacts.
 276          $this->assertArrayHasKey(0, $result);
 277          $this->assertArrayHasKey(1, $result);
 278          $contacts = $result[0];
 279          $noncontacts = $result[1];
 280  
 281          // Check that we retrieved the correct contacts.
 282          $this->assertCount(2, $contacts);
 283          $this->assertEquals($users[2]->id, $contacts[0]->id);
 284          $this->assertEquals($users[3]->id, $contacts[1]->id);
 285  
 286          // Verify the correct conversations were returned for the contacts.
 287          $this->assertCount(2, $contacts[0]->conversations);
 288          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]->conversations[$gc1->id]->type);
 289          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]->conversations[$ic1->id]->type);
 290  
 291          $this->assertCount(0, $contacts[1]->conversations);
 292  
 293          // Check that we retrieved the correct non-contacts.
 294          // If site wide messaging is enabled, we expect to only be able to search for users whose profiles we can view.
 295          // In this case, as a student, that's the course contact for course2 and those noncontacts sharing a course with user1.
 296          // Consider first conversations is self-conversation.
 297          $this->assertCount(4, $noncontacts);
 298          $this->assertEquals($users[1]->id, $noncontacts[0]->id);
 299          $this->assertEquals($users[6]->id, $noncontacts[1]->id);
 300          $this->assertEquals($users[7]->id, $noncontacts[2]->id);
 301          $this->assertEquals($users[9]->id, $noncontacts[3]->id);
 302  
 303          $this->assertCount(1, $noncontacts[1]->conversations);
 304          $this->assertCount(1, $noncontacts[2]->conversations);
 305          $this->assertCount(0, $noncontacts[3]->conversations);
 306  
 307          // Verify the correct conversations were returned for the non-contacts.
 308          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_SELF,
 309              $noncontacts[0]->conversations[$selfconversation->id]->type);
 310  
 311          $this->assertCount(1, $noncontacts[1]->conversations);
 312          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 313              $noncontacts[1]->conversations[$ic2->id]->type);
 314  
 315          $this->assertCount(1, $noncontacts[2]->conversations);
 316          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]->conversations[$gc1->id]->type);
 317  
 318          $this->assertCount(0, $noncontacts[3]->conversations);
 319      }
 320  
 321      /**
 322       * Verify searching for users find themselves when they have self-conversations.
 323       */
 324      public function test_message_search_users_self_conversations() {
 325          $this->resetAfterTest();
 326  
 327          // Create some users.
 328          $user1 = new \stdClass();
 329          $user1->firstname = 'User';
 330          $user1->lastname = 'One';
 331          $user1 = $this->getDataGenerator()->create_user($user1);
 332          $user2 = new \stdClass();
 333          $user2->firstname = 'User';
 334          $user2->lastname = 'Two';
 335          $user2 = $this->getDataGenerator()->create_user($user2);
 336  
 337          // Get self-conversation for user1.
 338          $sc1 = api::get_self_conversation($user1->id);
 339          testhelper::send_fake_message_to_conversation($user1, $sc1->id, 'Hi myself!');
 340  
 341          // Perform a search as user1.
 342          $this->setUser($user1);
 343          $result = api::message_search_users($user1->id, 'One');
 344  
 345          // Check user1 is found as non-contacts.
 346          $this->assertCount(0, $result[0]);
 347          $this->assertCount(1, $result[1]);
 348      }
 349  
 350      /**
 351       * Verify searching for users works even if no matching users from either contacts, or non-contacts can be found.
 352       */
 353      public function test_message_search_users_with_empty_result() {
 354          $this->resetAfterTest();
 355  
 356          // Create some users, but make sure neither will match the search term.
 357          $user1 = new \stdClass();
 358          $user1->firstname = 'User';
 359          $user1->lastname = 'One';
 360          $user1 = $this->getDataGenerator()->create_user($user1);
 361          $user2 = new \stdClass();
 362          $user2->firstname = 'User';
 363          $user2->lastname = 'Two';
 364          $user2 = $this->getDataGenerator()->create_user($user2);
 365  
 366          // Perform a search as user1.
 367          $this->setUser($user1);
 368          $result = api::message_search_users($user1->id, 'search');
 369  
 370          // Check results are empty.
 371          $this->assertCount(0, $result[0]);
 372          $this->assertCount(0, $result[1]);
 373      }
 374  
 375      /**
 376       * Test verifying that limits and offsets work for both the contacts and non-contacts return data.
 377       */
 378      public function test_message_search_users_limit_offset() {
 379          $this->resetAfterTest();
 380  
 381          // Create 20 users.
 382          $users = [];
 383          foreach (range(1, 20) as $i) {
 384              $user = new \stdClass();
 385              $user->firstname = "User search";
 386              $user->lastname = $i;
 387              $user = $this->getDataGenerator()->create_user($user);
 388              $users[$i] = $user;
 389          }
 390  
 391          // Enrol the first 9 users in the same course, but leave them as non-contacts.
 392          $this->setAdminUser();
 393          $course1 = $this->getDataGenerator()->create_course();
 394          foreach (range(1, 8) as $i) {
 395              $this->getDataGenerator()->enrol_user($users[$i]->id, $course1->id);
 396          }
 397  
 398          // Add 5 users, starting at the 11th user, as contacts for user1.
 399          foreach (range(11, 15) as $i) {
 400              api::add_contact($users[1]->id, $users[$i]->id);
 401          }
 402  
 403          // Set as the user performing the search.
 404          $this->setUser($users[1]);
 405  
 406          // Search using a limit of 3.
 407          // This tests the case where we have more results than the limit for both contacts and non-contacts.
 408          $result = api::message_search_users($users[1]->id, 'search', 0, 3);
 409          $contacts = $result[0];
 410          $noncontacts = $result[1];
 411  
 412          // Check that we retrieved the correct contacts.
 413          $this->assertCount(3, $contacts);
 414          $this->assertEquals($users[11]->id, $contacts[0]->id);
 415          $this->assertEquals($users[12]->id, $contacts[1]->id);
 416          $this->assertEquals($users[13]->id, $contacts[2]->id);
 417  
 418          // Check that we retrieved the correct non-contacts.
 419          // Consider first conversations is self-conversation.
 420          $this->assertCount(3, $noncontacts);
 421          $this->assertEquals($users[1]->id, $noncontacts[0]->id);
 422          $this->assertEquals($users[2]->id, $noncontacts[1]->id);
 423          $this->assertEquals($users[3]->id, $noncontacts[2]->id);
 424  
 425          // Now, offset to get the next batch of results.
 426          // We expect to see 2 contacts, and 3 non-contacts.
 427          $result = api::message_search_users($users[1]->id, 'search', 3, 3);
 428          $contacts = $result[0];
 429          $noncontacts = $result[1];
 430          $this->assertCount(2, $contacts);
 431          $this->assertEquals($users[14]->id, $contacts[0]->id);
 432          $this->assertEquals($users[15]->id, $contacts[1]->id);
 433  
 434          $this->assertCount(3, $noncontacts);
 435          $this->assertEquals($users[4]->id, $noncontacts[0]->id);
 436          $this->assertEquals($users[5]->id, $noncontacts[1]->id);
 437          $this->assertEquals($users[6]->id, $noncontacts[2]->id);
 438  
 439          // Now, offset to get the next batch of results.
 440          // We expect to see 0 contacts, and 2 non-contacts.
 441          $result = api::message_search_users($users[1]->id, 'search', 6, 3);
 442          $contacts = $result[0];
 443          $noncontacts = $result[1];
 444          $this->assertCount(0, $contacts);
 445  
 446          $this->assertCount(2, $noncontacts);
 447          $this->assertEquals($users[7]->id, $noncontacts[0]->id);
 448          $this->assertEquals($users[8]->id, $noncontacts[1]->id);
 449      }
 450  
 451      /**
 452       * Tests searching users as a user having the 'moodle/user:viewdetails' capability.
 453       */
 454      public function test_message_search_users_with_cap() {
 455          $this->resetAfterTest();
 456          global $DB;
 457  
 458          // Create some users.
 459          $users = [];
 460          foreach (range(1, 8) as $i) {
 461              $user = new \stdClass();
 462              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
 463              $user->lastname = $i;
 464              $user = $this->getDataGenerator()->create_user($user);
 465              $users[$i] = $user;
 466          }
 467  
 468          // Enrol a few users in the same course, but leave them as non-contacts.
 469          $course1 = $this->getDataGenerator()->create_course();
 470          $this->setAdminUser();
 471          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
 472          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
 473          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
 474  
 475          // Add some other users as contacts.
 476          api::add_contact($users[1]->id, $users[2]->id);
 477          api::add_contact($users[3]->id, $users[1]->id);
 478          api::add_contact($users[1]->id, $users[4]->id);
 479  
 480          // Set as the user performing the search.
 481          $this->setUser($users[1]);
 482  
 483          // Grant the authenticated user role the capability 'user:viewdetails' at site context.
 484          $authenticatedrole = $DB->get_record('role', ['shortname' => 'user'], '*', MUST_EXIST);
 485          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $authenticatedrole->id, \context_system::instance());
 486  
 487          // Perform a search with $CFG->messagingallusers disabled.
 488          set_config('messagingallusers', 0);
 489          $result = api::message_search_users($users[1]->id, 'search');
 490          $contacts = $result[0];
 491          $noncontacts = $result[1];
 492  
 493          // Check that we retrieved the correct contacts.
 494          $this->assertCount(2, $contacts);
 495          $this->assertEquals($users[2]->id, $contacts[0]->id);
 496          $this->assertEquals($users[3]->id, $contacts[1]->id);
 497  
 498          // Check that we retrieved the correct non-contacts.
 499          // Site-wide messaging is disabled, so we expect to be able to search for any users whose profiles we can view.
 500          // Consider first conversations is self-conversation.
 501          $this->assertCount(3, $noncontacts);
 502          $this->assertEquals($users[1]->id, $noncontacts[0]->id);
 503          $this->assertEquals($users[6]->id, $noncontacts[1]->id);
 504          $this->assertEquals($users[7]->id, $noncontacts[2]->id);
 505      }
 506  
 507      /**
 508       * Tests searching users with messaging disabled.
 509       */
 510      public function test_message_search_users_messaging_disabled() {
 511          $this->resetAfterTest();
 512  
 513          // Create a user.
 514          $user = $this->getDataGenerator()->create_user();
 515  
 516          // Disable messaging.
 517          set_config('messaging', 0);
 518  
 519          // Ensure an exception is thrown.
 520          $this->expectException('moodle_exception');
 521          api::message_search_users($user->id, 'User');
 522      }
 523  
 524      /**
 525       * Tests getting conversations between 2 users.
 526       */
 527      public function test_get_conversations_between_users() {
 528          // Create some users.
 529          $user1 = new \stdClass();
 530          $user1->firstname = 'User';
 531          $user1->lastname = 'One';
 532          $user1 = self::getDataGenerator()->create_user($user1);
 533  
 534          $user2 = new \stdClass();
 535          $user2->firstname = 'User';
 536          $user2->lastname = 'Two';
 537          $user2 = self::getDataGenerator()->create_user($user2);
 538  
 539          $user3 = new \stdClass();
 540          $user3->firstname = 'User search';
 541          $user3->lastname = 'Three';
 542          $user3 = self::getDataGenerator()->create_user($user3);
 543  
 544          $user4 = new \stdClass();
 545          $user4->firstname = 'User';
 546          $user4->lastname = 'Four';
 547          $user4 = self::getDataGenerator()->create_user($user4);
 548  
 549          $user5 = new \stdClass();
 550          $user5->firstname = 'User';
 551          $user5->lastname = 'Five';
 552          $user5 = self::getDataGenerator()->create_user($user5);
 553  
 554          $user6 = new \stdClass();
 555          $user6->firstname = 'User search';
 556          $user6->lastname = 'Six';
 557          $user6 = self::getDataGenerator()->create_user($user6);
 558  
 559          // Add some users as contacts.
 560          api::add_contact($user1->id, $user2->id);
 561          api::add_contact($user6->id, $user1->id);
 562  
 563          // Create private conversations with some users.
 564          api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 565              array($user1->id, $user2->id));
 566          api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 567              array($user3->id, $user1->id));
 568  
 569          // Create a group conversation with users.
 570          api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
 571              array($user1->id, $user2->id, $user3->id, $user4->id),
 572              'Project chat');
 573  
 574          // Check that we retrieved the correct conversations.
 575          $this->assertCount(2, api::get_conversations_between_users($user1->id, $user2->id));
 576          $this->assertCount(2, api::get_conversations_between_users($user2->id, $user1->id));
 577          $this->assertCount(2, api::get_conversations_between_users($user1->id, $user3->id));
 578          $this->assertCount(2, api::get_conversations_between_users($user3->id, $user1->id));
 579          $this->assertCount(1, api::get_conversations_between_users($user1->id, $user4->id));
 580          $this->assertCount(1, api::get_conversations_between_users($user4->id, $user1->id));
 581          $this->assertCount(0, api::get_conversations_between_users($user1->id, $user5->id));
 582          $this->assertCount(0, api::get_conversations_between_users($user5->id, $user1->id));
 583          $this->assertCount(0, api::get_conversations_between_users($user1->id, $user6->id));
 584          $this->assertCount(0, api::get_conversations_between_users($user6->id, $user1->id));
 585      }
 586  
 587      /**
 588       * Tests getting self-conversations.
 589       */
 590      public function test_get_self_conversation() {
 591          // Create some users.
 592          $user1 = new \stdClass();
 593          $user1->firstname = 'User';
 594          $user1->lastname = 'One';
 595          $user1 = self::getDataGenerator()->create_user($user1);
 596  
 597          $user2 = new \stdClass();
 598          $user2->firstname = 'User';
 599          $user2->lastname = 'Two';
 600          $user2 = self::getDataGenerator()->create_user($user2);
 601  
 602          $user3 = new \stdClass();
 603          $user3->firstname = 'User search';
 604          $user3->lastname = 'Three';
 605          $user3 = self::getDataGenerator()->create_user($user3);
 606  
 607          // Add some users as contacts.
 608          api::add_contact($user1->id, $user2->id);
 609          api::add_contact($user3->id, $user1->id);
 610  
 611          // Create private conversations with some users.
 612          api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 613              array($user1->id, $user2->id));
 614          api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 615              array($user3->id, $user1->id));
 616  
 617          // Create a group conversation with users.
 618          $gc = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
 619              array($user1->id, $user2->id, $user3->id),
 620              'Project chat');
 621  
 622          // Get self-conversations.
 623          $rsc1 = api::get_self_conversation($user1->id);
 624          $rsc2 = api::get_self_conversation($user2->id);
 625          $rsc3 = api::get_self_conversation($user3->id);
 626  
 627          // Send message to self-conversation.
 628          testhelper::send_fake_message_to_conversation($user1, $rsc1->id, 'Message to myself!');
 629  
 630          // Check that we retrieved the correct conversations.
 631          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_SELF, $rsc1->type);
 632          $members = api::get_conversation_members($user1->id, $rsc1->id);
 633          $this->assertCount(1, $members);
 634          $member = reset($members);
 635          $this->assertEquals($user1->id, $member->id);
 636  
 637          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_SELF, $rsc2->type);
 638          $members = api::get_conversation_members($user2->id, $rsc2->id);
 639          $this->assertCount(1, $members);
 640          $member = reset($members);
 641          $this->assertEquals($user2->id, $member->id);
 642  
 643          api::delete_all_conversation_data($rsc3->id);
 644          $selfconversation = api::get_self_conversation($user3->id);
 645          $members = api::get_conversation_members($user1->id, $selfconversation->id);
 646          $this->assertCount(1, $members);
 647      }
 648  
 649      /**
 650       * Tests searching messages.
 651       */
 652      public function test_search_messages() {
 653          $this->resetAfterTest();
 654  
 655          // Create some users.
 656          $user1 = self::getDataGenerator()->create_user();
 657          $user2 = self::getDataGenerator()->create_user();
 658          $user3 = self::getDataGenerator()->create_user();
 659  
 660          // The person doing the search.
 661          $this->setUser($user1);
 662  
 663          // Get self-conversation.
 664          $sc = api::get_self_conversation($user1->id);
 665  
 666          // Create group conversation.
 667          $gc = api::create_conversation(
 668              api::MESSAGE_CONVERSATION_TYPE_GROUP,
 669              [$user1->id, $user2->id, $user3->id]
 670          );
 671  
 672          // Send some messages back and forth.
 673          $time = 1;
 674          testhelper::send_fake_message_to_conversation($user1, $sc->id, 'Test message to self!', $time);
 675          testhelper::send_fake_message_to_conversation($user1, $gc->id, 'My hero!', $time + 1);
 676          $this->send_fake_message($user3, $user1, 'Don\'t block me.', 0, $time + 2);
 677          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 3);
 678          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 4);
 679          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 5);
 680          $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 6);
 681  
 682          $convid = api::get_conversation_between_users([$user1->id, $user2->id]);
 683          $conv2id = api::get_conversation_between_users([$user1->id, $user3->id]);
 684  
 685          // Block user 3.
 686          api::block_user($user1->id, $user3->id);
 687  
 688          // Perform a search.
 689          $messages = api::search_messages($user1->id, 'o');
 690  
 691          // Confirm the data is correct.
 692          $this->assertEquals(5, count($messages));
 693          $message1 = $messages[0];
 694          $message2 = $messages[1];
 695          $message3 = $messages[2];
 696          $message4 = $messages[3];
 697          $message5 = $messages[4];
 698  
 699          $this->assertEquals($user2->id, $message1->userid);
 700          $this->assertEquals($user2->id, $message1->useridfrom);
 701          $this->assertEquals(fullname($user2), $message1->fullname);
 702          $this->assertTrue($message1->ismessaging);
 703          $this->assertEquals('Word.', $message1->lastmessage);
 704          $this->assertNotEmpty($message1->messageid);
 705          $this->assertNull($message1->isonline);
 706          $this->assertFalse($message1->isread);
 707          $this->assertFalse($message1->isblocked);
 708          $this->assertNull($message1->unreadcount);
 709          $this->assertEquals($convid, $message1->conversationid);
 710  
 711          $this->assertEquals($user2->id, $message2->userid);
 712          $this->assertEquals($user1->id, $message2->useridfrom);
 713          $this->assertEquals(fullname($user2), $message2->fullname);
 714          $this->assertTrue($message2->ismessaging);
 715          $this->assertEquals('Yo!', $message2->lastmessage);
 716          $this->assertNotEmpty($message2->messageid);
 717          $this->assertNull($message2->isonline);
 718          $this->assertTrue($message2->isread);
 719          $this->assertFalse($message2->isblocked);
 720          $this->assertNull($message2->unreadcount);
 721          $this->assertEquals($convid, $message2->conversationid);
 722  
 723          $this->assertEquals($user3->id, $message3->userid);
 724          $this->assertEquals($user3->id, $message3->useridfrom);
 725          $this->assertEquals(fullname($user3), $message3->fullname);
 726          $this->assertTrue($message3->ismessaging);
 727          $this->assertEquals('Don\'t block me.', $message3->lastmessage);
 728          $this->assertNotEmpty($message3->messageid);
 729          $this->assertNull($message3->isonline);
 730          $this->assertFalse($message3->isread);
 731          $this->assertTrue($message3->isblocked);
 732          $this->assertNull($message3->unreadcount);
 733          $this->assertEquals($conv2id, $message3->conversationid);
 734  
 735          // This is a group conversation. For now, search_messages returns only one of the other users on the conversation. It can't
 736          // be guaranteed who will be returned in the first place, so we need to use the in_array to check all the possibilities.
 737          $this->assertTrue(in_array($message4->userid, [$user2->id, $user3->id]));
 738          $this->assertEquals($user1->id, $message4->useridfrom);
 739          $this->assertTrue($message4->ismessaging);
 740          $this->assertEquals('My hero!', $message4->lastmessage);
 741          $this->assertNotEmpty($message4->messageid);
 742          $this->assertNull($message4->isonline);
 743          $this->assertTrue($message4->isread);
 744          $this->assertNull($message4->unreadcount);
 745          $this->assertEquals($gc->id, $message4->conversationid);
 746  
 747          $this->assertEquals($user1->id, $message5->userid);
 748          $this->assertEquals($user1->id, $message5->useridfrom);
 749          $this->assertEquals(fullname($user1), $message5->fullname);
 750          $this->assertTrue($message5->ismessaging);
 751          $this->assertEquals('Test message to self!', $message5->lastmessage);
 752          $this->assertNotEmpty($message5->messageid);
 753          $this->assertFalse($message5->isonline);
 754          $this->assertTrue($message5->isread);
 755          $this->assertFalse($message5->isblocked);
 756          $this->assertNull($message5->unreadcount);
 757          $this->assertEquals($sc->id, $message5->conversationid);
 758      }
 759  
 760      /**
 761       * Test verifying that favourited conversations can be retrieved.
 762       */
 763      public function test_get_favourite_conversations() {
 764          // Create some users.
 765          $user1 = self::getDataGenerator()->create_user();
 766          $user2 = self::getDataGenerator()->create_user();
 767          $user3 = self::getDataGenerator()->create_user();
 768          $user4 = self::getDataGenerator()->create_user();
 769  
 770          // The person doing the search.
 771          $this->setUser($user1);
 772  
 773          // Only self-conversation created.
 774          $this->assertCount(1, api::get_conversations($user1->id));
 775  
 776          // Create some conversations for user1.
 777          $time = 1;
 778          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
 779          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
 780          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
 781          $messageid1 = $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
 782  
 783          $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
 784          $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
 785          $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
 786          $messageid2 = $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
 787  
 788          $this->send_fake_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 9);
 789          $this->send_fake_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 10);
 790          $messageid3 = $this->send_fake_message($user1, $user4, 'Dope.', 0, $time + 11);
 791  
 792          // Favourite the first 2 conversations for user1.
 793          $convoids = [];
 794          $convoids[] = api::get_conversation_between_users([$user1->id, $user2->id]);
 795          $convoids[] = api::get_conversation_between_users([$user1->id, $user3->id]);
 796          $user1context = \context_user::instance($user1->id);
 797          $service = \core_favourites\service_factory::get_service_for_user_context($user1context);
 798          foreach ($convoids as $convoid) {
 799              $service->create_favourite('core_message', 'message_conversations', $convoid, $user1context);
 800          }
 801  
 802          // We should have 4 conversations.
 803          // Consider first conversations is self-conversation.
 804          $this->assertCount(4, api::get_conversations($user1->id));
 805  
 806          // And 3 favourited conversations (self-conversation included).
 807          $conversations = api::get_conversations($user1->id, 0, 20, null, true);
 808          $this->assertCount(3, $conversations);
 809          $conversations = api::get_conversations(
 810              $user1->id,
 811              0,
 812              20,
 813              api::MESSAGE_CONVERSATION_TYPE_SELF,
 814              true
 815          );
 816          $this->assertCount(1, $conversations);
 817      }
 818  
 819      /**
 820       * Tests retrieving favourite conversations with a limit and offset to ensure pagination works correctly.
 821       */
 822      public function test_get_favourite_conversations_limit_offset() {
 823          // Create some users.
 824          $user1 = self::getDataGenerator()->create_user();
 825          $user2 = self::getDataGenerator()->create_user();
 826          $user3 = self::getDataGenerator()->create_user();
 827          $user4 = self::getDataGenerator()->create_user();
 828  
 829          // The person doing the search.
 830          $this->setUser($user1);
 831  
 832          // Only self-conversation created.
 833          $this->assertCount(1, api::get_conversations($user1->id));
 834  
 835          // Create some conversations for user1.
 836          $time = 1;
 837          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
 838          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
 839          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
 840          $messageid1 = $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
 841  
 842          $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
 843          $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
 844          $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
 845          $messageid2 = $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
 846  
 847          $this->send_fake_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 9);
 848          $this->send_fake_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 10);
 849          $messageid3 = $this->send_fake_message($user1, $user4, 'Dope.', 0, $time + 11);
 850  
 851          // Favourite the all conversations for user1.
 852          $convoids = [];
 853          $convoids[] = api::get_conversation_between_users([$user1->id, $user2->id]);
 854          $convoids[] = api::get_conversation_between_users([$user1->id, $user3->id]);
 855          $convoids[] = api::get_conversation_between_users([$user1->id, $user4->id]);
 856          $user1context = \context_user::instance($user1->id);
 857          $service = \core_favourites\service_factory::get_service_for_user_context($user1context);
 858          foreach ($convoids as $convoid) {
 859              $service->create_favourite('core_message', 'message_conversations', $convoid, $user1context);
 860          }
 861  
 862          // Consider first conversations is self-conversation.
 863          // Get all records, using offset 0 and large limit.
 864          $this->assertCount(4, api::get_conversations($user1->id, 0, 20, null, true));
 865  
 866          // Now, get 10 conversations starting at the second record. We should see 2 conversations.
 867          $this->assertCount(3, api::get_conversations($user1->id, 1, 10, null, true));
 868  
 869          // Now, try to get favourited conversations using an invalid offset.
 870          $this->assertCount(0, api::get_conversations($user1->id, 5, 10, null, true));
 871      }
 872  
 873      /**
 874       * Tests retrieving favourite conversations when a conversation contains a deleted user.
 875       */
 876      public function test_get_favourite_conversations_with_deleted_user() {
 877          // Create some users.
 878          $user1 = self::getDataGenerator()->create_user();
 879          $user2 = self::getDataGenerator()->create_user();
 880          $user3 = self::getDataGenerator()->create_user();
 881  
 882          // Send some messages back and forth, have some different conversations with different users.
 883          $time = 1;
 884          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
 885          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
 886          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
 887          $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
 888  
 889          $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
 890          $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
 891          $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
 892          $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
 893  
 894          // Favourite the all conversations for user1.
 895          $convoids = [];
 896          $convoids[] = api::get_conversation_between_users([$user1->id, $user2->id]);
 897          $convoids[] = api::get_conversation_between_users([$user1->id, $user3->id]);
 898          $user1context = \context_user::instance($user1->id);
 899          $service = \core_favourites\service_factory::get_service_for_user_context($user1context);
 900          foreach ($convoids as $convoid) {
 901              $service->create_favourite('core_message', 'message_conversations', $convoid, $user1context);
 902          }
 903  
 904          // Delete the second user.
 905          delete_user($user2);
 906  
 907          // Retrieve the conversations.
 908          $conversations = api::get_conversations($user1->id, 0, 20, null, true);
 909  
 910          // We should have both conversations, despite the other user being soft-deleted.
 911          // Consider first conversations is self-conversation.
 912          $this->assertCount(3, $conversations);
 913  
 914          // Confirm the conversation is from the non-deleted user.
 915          $conversation = reset($conversations);
 916          $this->assertEquals($convoids[1], $conversation->id);
 917      }
 918  
 919      /**
 920       * Test confirming that conversations can be marked as favourites.
 921       */
 922      public function test_set_favourite_conversation() {
 923          // Create some users.
 924          $user1 = self::getDataGenerator()->create_user();
 925          $user2 = self::getDataGenerator()->create_user();
 926          $user3 = self::getDataGenerator()->create_user();
 927  
 928          // Send some messages back and forth, have some different conversations with different users.
 929          $time = 1;
 930          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
 931          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
 932          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
 933          $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
 934  
 935          $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
 936          $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
 937          $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
 938          $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
 939  
 940          // Favourite the first conversation as user 1.
 941          $conversationid1 = api::get_conversation_between_users([$user1->id, $user2->id]);
 942          $favourite = api::set_favourite_conversation($conversationid1, $user1->id);
 943  
 944          // Verify we have two favourite conversations a user 1.
 945          // Consider first conversations is self-conversation.
 946          $this->assertCount(2, api::get_conversations($user1->id, 0, 20, null, true));
 947  
 948          // Verify we have only one favourite as user2, despite being a member in that conversation.
 949          // Consider first conversations is self-conversation.
 950          $this->assertCount(1, api::get_conversations($user2->id, 0, 20, null, true));
 951  
 952          // Try to favourite the same conversation again should just return the existing favourite.
 953          $repeatresult = api::set_favourite_conversation($conversationid1, $user1->id);
 954          $this->assertEquals($favourite->id, $repeatresult->id);
 955      }
 956  
 957      /**
 958       * Test verifying that trying to mark a non-existent conversation as a favourite, results in an exception.
 959       */
 960      public function test_set_favourite_conversation_nonexistent_conversation() {
 961          // Create some users.
 962          $user1 = self::getDataGenerator()->create_user();
 963          // Try to favourite a non-existent conversation.
 964          $this->expectException(\moodle_exception::class);
 965          api::set_favourite_conversation(0, $user1->id);
 966      }
 967  
 968      /**
 969       * Test verifying that a conversation cannot be marked as favourite unless the user is a member of that conversation.
 970       */
 971      public function test_set_favourite_conversation_non_member() {
 972          // Create some users.
 973          $user1 = self::getDataGenerator()->create_user();
 974          $user2 = self::getDataGenerator()->create_user();
 975          $user3 = self::getDataGenerator()->create_user();
 976  
 977          // Send some messages back and forth, have some different conversations with different users.
 978          $time = 1;
 979          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
 980          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
 981          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
 982          $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
 983  
 984          $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
 985          $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
 986          $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
 987          $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
 988  
 989          // Try to favourite the first conversation as user 3, who is not a member.
 990          $conversationid1 = api::get_conversation_between_users([$user1->id, $user2->id]);
 991          $this->expectException(\moodle_exception::class);
 992          api::set_favourite_conversation($conversationid1, $user3->id);
 993      }
 994  
 995      /**
 996       * Test confirming that those conversations marked as favourites can be unfavourited.
 997       */
 998      public function test_unset_favourite_conversation() {
 999          // Create some users.
1000          $user1 = self::getDataGenerator()->create_user();
1001          $user2 = self::getDataGenerator()->create_user();
1002          $user3 = self::getDataGenerator()->create_user();
1003  
1004          // Send some messages back and forth, have some different conversations with different users.
1005          $time = 1;
1006          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
1007          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
1008          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
1009          $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
1010  
1011          $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
1012          $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
1013          $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
1014          $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
1015  
1016          // Favourite the first conversation as user 1 and the second as user 3.
1017          $conversationid1 = api::get_conversation_between_users([$user1->id, $user2->id]);
1018          $conversationid2 = api::get_conversation_between_users([$user1->id, $user3->id]);
1019          api::set_favourite_conversation($conversationid1, $user1->id);
1020          api::set_favourite_conversation($conversationid2, $user3->id);
1021  
1022          // Verify we have two favourite conversations for both user 1 and user 3, counting self conversations.
1023          $this->assertCount(2, api::get_conversations($user1->id, 0, 20, null, true));
1024          $this->assertCount(2, api::get_conversations($user3->id, 0, 20, null, true));
1025  
1026          // Now unfavourite the conversation as user 1.
1027          api::unset_favourite_conversation($conversationid1, $user1->id);
1028  
1029          // Verify we have two favourite conversations user 3 only, and one for user1, counting self conversations.
1030          $this->assertCount(2, api::get_conversations($user3->id, 0, 20, null, true));
1031          $this->assertCount(1, api::get_conversations($user1->id, 0, 20, null, true));
1032  
1033          // Try to favourite the same conversation again as user 1.
1034          $this->expectException(\moodle_exception::class);
1035          api::unset_favourite_conversation($conversationid1, $user1->id);
1036      }
1037  
1038      /**
1039       * Test verifying that a valid conversation cannot be unset as a favourite if it's not marked as a favourite.
1040       */
1041      public function test_unset_favourite_conversation_not_favourite() {
1042          // Create some users.
1043          $user1 = self::getDataGenerator()->create_user();
1044          $user2 = self::getDataGenerator()->create_user();
1045  
1046          // Send some messages back and forth, have some different conversations with different users.
1047          $time = 1;
1048          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
1049          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
1050          $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
1051          $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
1052  
1053          // Now try to unfavourite the conversation as user 1.
1054          $conversationid1 = api::get_conversation_between_users([$user1->id, $user2->id]);
1055          $this->expectException(\moodle_exception::class);
1056          api::unset_favourite_conversation($conversationid1, $user1->id);
1057      }
1058  
1059      /**
1060       * Test verifying that a non-existent conversation cannot be unset as a favourite.
1061       */
1062      public function test_unset_favourite_conversation_non_existent_conversation() {
1063          // Create some users.
1064          $user1 = self::getDataGenerator()->create_user();
1065  
1066          // Now try to unfavourite the conversation as user 1.
1067          $this->expectException(\moodle_exception::class);
1068          api::unset_favourite_conversation(0, $user1->id);
1069      }
1070  
1071      /**
1072       * Helper to seed the database with initial state.
1073       */
1074      protected function create_conversation_test_data() {
1075          // Create some users.
1076          $user1 = self::getDataGenerator()->create_user();
1077          $user2 = self::getDataGenerator()->create_user();
1078          $user3 = self::getDataGenerator()->create_user();
1079          $user4 = self::getDataGenerator()->create_user();
1080  
1081          $time = 1;
1082  
1083          // Create some conversations. We want:
1084          // 1) At least one of each type (group, individual) of which user1 IS a member and DID send the most recent message.
1085          // 2) At least one of each type (group, individual) of which user1 IS a member and DID NOT send the most recent message.
1086          // 3) At least one of each type (group, individual) of which user1 IS NOT a member.
1087          // 4) At least two group conversation having 0 messages, of which user1 IS a member (To confirm conversationid ordering).
1088          // 5) At least one group conversation having 0 messages, of which user1 IS NOT a member.
1089  
1090          // Individual conversation, user1 is a member, last message from other user.
1091          $ic1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1092              [$user1->id, $user2->id]);
1093          testhelper::send_fake_message_to_conversation($user1, $ic1->id, 'Message 1', $time);
1094          testhelper::send_fake_message_to_conversation($user2, $ic1->id, 'Message 2', $time + 1);
1095  
1096          // Individual conversation, user1 is a member, last message from user1.
1097          $ic2 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1098              [$user1->id, $user3->id]);
1099          testhelper::send_fake_message_to_conversation($user3, $ic2->id, 'Message 3', $time + 2);
1100          testhelper::send_fake_message_to_conversation($user1, $ic2->id, 'Message 4', $time + 3);
1101  
1102          // Individual conversation, user1 is not a member.
1103          $ic3 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1104              [$user2->id, $user3->id]);
1105          testhelper::send_fake_message_to_conversation($user2, $ic3->id, 'Message 5', $time + 4);
1106          testhelper::send_fake_message_to_conversation($user3, $ic3->id, 'Message 6', $time + 5);
1107  
1108          // Group conversation, user1 is not a member.
1109          $gc1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1110              [$user2->id, $user3->id, $user4->id], 'Project discussions');
1111          testhelper::send_fake_message_to_conversation($user2, $gc1->id, 'Message 7', $time + 6);
1112          testhelper::send_fake_message_to_conversation($user4, $gc1->id, 'Message 8', $time + 7);
1113  
1114          // Group conversation, user1 is a member, last message from another user.
1115          $gc2 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1116              [$user1->id, $user3->id, $user4->id], 'Group chat');
1117          testhelper::send_fake_message_to_conversation($user1, $gc2->id, 'Message 9', $time + 8);
1118          testhelper::send_fake_message_to_conversation($user3, $gc2->id, 'Message 10', $time + 9);
1119          testhelper::send_fake_message_to_conversation($user4, $gc2->id, 'Message 11', $time + 10);
1120  
1121          // Group conversation, user1 is a member, last message from user1.
1122          $gc3 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1123              [$user1->id, $user2->id, $user3->id, $user4->id], 'Group chat again!');
1124          testhelper::send_fake_message_to_conversation($user4, $gc3->id, 'Message 12', $time + 11);
1125          testhelper::send_fake_message_to_conversation($user3, $gc3->id, 'Message 13', $time + 12);
1126          testhelper::send_fake_message_to_conversation($user1, $gc3->id, 'Message 14', $time + 13);
1127  
1128          // Empty group conversations (x2), user1 is a member.
1129          $gc4 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1130              [$user1->id, $user2->id, $user3->id], 'Empty group');
1131          $gc5 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1132              [$user1->id, $user2->id, $user4->id], 'Another empty group');
1133  
1134          // Empty group conversation, user1 is NOT a member.
1135          $gc6 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1136              [$user2->id, $user3->id, $user4->id], 'Empty group 3');
1137  
1138          return [$user1, $user2, $user3, $user4, $ic1, $ic2, $ic3, $gc1, $gc2, $gc3, $gc4, $gc5, $gc6];
1139      }
1140  
1141      /**
1142       * Test verifying get_conversations when no limits, offsets, type filters or favourite restrictions are used.
1143       */
1144      public function test_get_conversations_no_restrictions() {
1145          global $DB;
1146  
1147          $user1 = self::getDataGenerator()->create_user();
1148          // Self-conversation should exists.
1149          $this->assertCount(1, api::get_conversations($user1->id));
1150  
1151          // Get a bunch of conversations, some group, some individual and in different states.
1152          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
1153              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
1154  
1155          // Get all conversations for user1.
1156          $conversations = api::get_conversations($user1->id);
1157  
1158          // Verify there are 2 individual conversation, 2 group conversations, 2 empty group conversations,
1159          // and a self-conversation.
1160          // The conversations with the most recent messages should be listed first, followed by the empty
1161          // conversations, with the most recently created first.
1162          $this->assertCount(7, $conversations);
1163          $typecounts  = array_count_values(array_column($conversations, 'type'));
1164          $this->assertEquals(2, $typecounts[1]);
1165          $this->assertEquals(4, $typecounts[2]);
1166          $this->assertEquals(1, $typecounts[3]);
1167  
1168          // Those conversations having messages should be listed after self-conversation, ordered by most recent message time.
1169          $this->assertEquals($gc3->id, $conversations[0]->id);
1170          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $conversations[0]->type);
1171          $this->assertFalse($conversations[1]->isfavourite);
1172          $this->assertCount(1, $conversations[0]->members);
1173          $this->assertEquals(4, $conversations[0]->membercount);
1174          $this->assertCount(1, $conversations[0]->messages);
1175          $message = $DB->get_record('messages', ['id' => $conversations[0]->messages[0]->id]);
1176          $expectedmessagetext = message_format_message_text($message);
1177          $this->assertEquals($expectedmessagetext, $conversations[0]->messages[0]->text);
1178          $this->assertEquals($user1->id, $conversations[0]->messages[0]->useridfrom);
1179  
1180          $this->assertEquals($gc2->id, $conversations[1]->id);
1181          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $conversations[1]->type);
1182          $this->assertFalse($conversations[1]->isfavourite);
1183          $this->assertCount(1, $conversations[1]->members);
1184          $this->assertEquals(3, $conversations[1]->membercount);
1185          $this->assertCount(1, $conversations[1]->messages);
1186          $message = $DB->get_record('messages', ['id' => $conversations[1]->messages[0]->id]);
1187          $expectedmessagetext = message_format_message_text($message);
1188          $this->assertEquals($expectedmessagetext, $conversations[1]->messages[0]->text);
1189          $this->assertEquals($user4->id, $conversations[1]->messages[0]->useridfrom);
1190  
1191          $this->assertEquals($ic2->id, $conversations[2]->id);
1192          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $conversations[2]->type);
1193          $this->assertFalse($conversations[2]->isfavourite);
1194          $this->assertCount(1, $conversations[2]->members);
1195          $this->assertEquals($user3->id, $conversations[2]->members[$user3->id]->id);
1196          $this->assertEquals(2, $conversations[2]->membercount);
1197          $this->assertCount(1, $conversations[2]->messages);
1198          $message = $DB->get_record('messages', ['id' => $conversations[2]->messages[0]->id]);
1199          $expectedmessagetext = message_format_message_text($message);
1200          $this->assertEquals($expectedmessagetext, $conversations[2]->messages[0]->text);
1201          $this->assertEquals($user1->id, $conversations[2]->messages[0]->useridfrom);
1202  
1203          $this->assertEquals($ic1->id, $conversations[3]->id);
1204          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $conversations[3]->type);
1205          $this->assertFalse($conversations[3]->isfavourite);
1206          $this->assertCount(1, $conversations[3]->members);
1207          $this->assertEquals(2, $conversations[3]->membercount);
1208          $this->assertCount(1, $conversations[3]->messages);
1209          $message = $DB->get_record('messages', ['id' => $conversations[3]->messages[0]->id]);
1210          $expectedmessagetext = message_format_message_text($message);
1211          $this->assertEquals($expectedmessagetext, $conversations[3]->messages[0]->text);
1212          $this->assertEquals($user2->id, $conversations[3]->messages[0]->useridfrom);
1213  
1214          // Of the groups without messages, we expect to see the most recently created first.
1215          $this->assertEquals($gc5->id, $conversations[4]->id);
1216          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $conversations[4]->type);
1217          $this->assertFalse($conversations[4]->isfavourite);
1218          $this->assertCount(0, $conversations[4]->members); // No members returned, because no recent messages exist.
1219          $this->assertEquals(3, $conversations[4]->membercount);
1220          $this->assertEmpty($conversations[4]->messages);
1221  
1222          $this->assertEquals($gc4->id, $conversations[5]->id);
1223          $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_GROUP, $conversations[5]->type);
1224          $this->assertFalse($conversations[5]->isfavourite);
1225          $this->assertCount(0, $conversations[5]->members);
1226          $this->assertEquals(3, $conversations[5]->membercount);
1227          $this->assertEmpty($conversations[5]->messages);
1228  
1229          // Verify format of the return structure.
1230          foreach ($conversations as $conv) {
1231              $this->assertObjectHasAttribute('id', $conv);
1232              $this->assertObjectHasAttribute('name', $conv);
1233              $this->assertObjectHasAttribute('subname', $conv);
1234              $this->assertObjectHasAttribute('imageurl', $conv);
1235              $this->assertObjectHasAttribute('type', $conv);
1236              $this->assertObjectHasAttribute('isfavourite', $conv);
1237              $this->assertObjectHasAttribute('membercount', $conv);
1238              $this->assertObjectHasAttribute('isread', $conv);
1239              $this->assertObjectHasAttribute('unreadcount', $conv);
1240              $this->assertObjectHasAttribute('members', $conv);
1241              foreach ($conv->members as $member) {
1242                  $this->assertObjectHasAttribute('id', $member);
1243                  $this->assertObjectHasAttribute('fullname', $member);
1244                  $this->assertObjectHasAttribute('profileimageurl', $member);
1245                  $this->assertObjectHasAttribute('profileimageurlsmall', $member);
1246                  $this->assertObjectHasAttribute('isonline', $member);
1247                  $this->assertObjectHasAttribute('showonlinestatus', $member);
1248                  $this->assertObjectHasAttribute('isblocked', $member);
1249                  $this->assertObjectHasAttribute('iscontact', $member);
1250                  $this->assertObjectHasAttribute('isdeleted', $member);
1251                  $this->assertObjectHasAttribute('canmessage', $member);
1252                  $this->assertObjectHasAttribute('requirescontact', $member);
1253                  $this->assertObjectHasAttribute('contactrequests', $member);
1254              }
1255              $this->assertObjectHasAttribute('messages', $conv);
1256              foreach ($conv->messages as $message) {
1257                  $this->assertObjectHasAttribute('id', $message);
1258                  $this->assertObjectHasAttribute('useridfrom', $message);
1259                  $this->assertObjectHasAttribute('text', $message);
1260                  $this->assertObjectHasAttribute('timecreated', $message);
1261              }
1262          }
1263      }
1264  
1265      /**
1266       * Test verifying that html format messages are supported, and that message_format_message_text() is being called appropriately.
1267       */
1268      public function test_get_conversations_message_format() {
1269          global $DB;
1270          // Create some users.
1271          $user1 = self::getDataGenerator()->create_user();
1272          $user2 = self::getDataGenerator()->create_user();
1273  
1274          // Create conversation.
1275          $conversation = api::create_conversation(
1276              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1277              [$user1->id, $user2->id]
1278          );
1279  
1280          // Send some messages back and forth.
1281          $time = 1;
1282          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Sup mang?', $time + 1);
1283          $mid = testhelper::send_fake_message_to_conversation($user1, $conversation->id, '<a href="#">A link</a>', $time + 2);
1284  
1285          // Verify the format of the html message.
1286          $message = $DB->get_record('messages', ['id' => $mid]);
1287          $expectedmessagetext = message_format_message_text($message);
1288          $conversations = api::get_conversations($user1->id);
1289          $messages = $conversations[0]->messages;
1290          $this->assertEquals($expectedmessagetext, $messages[0]->text);
1291      }
1292  
1293      /**
1294       * Test verifying get_conversations identifies if a conversation is muted or not.
1295       */
1296      public function test_get_conversations_some_muted() {
1297          // Create some users.
1298          $user1 = self::getDataGenerator()->create_user();
1299          $user2 = self::getDataGenerator()->create_user();
1300          $user3 = self::getDataGenerator()->create_user();
1301  
1302          $conversation1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1303              [$user1->id, $user2->id]);
1304          testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'Message 1');
1305          testhelper::send_fake_message_to_conversation($user2, $conversation1->id, 'Message 2');
1306          api::mute_conversation($user1->id, $conversation1->id);
1307  
1308          $conversation2 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1309              [$user1->id, $user3->id]);
1310          testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'Message 1');
1311          testhelper::send_fake_message_to_conversation($user2, $conversation2->id, 'Message 2');
1312  
1313          $conversation3 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1314              [$user1->id, $user2->id]);
1315          api::mute_conversation($user1->id, $conversation3->id);
1316  
1317          $conversation4 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_GROUP,
1318              [$user1->id, $user3->id]);
1319  
1320          $conversations = api::get_conversations($user1->id);
1321  
1322          usort($conversations, function($first, $second){
1323              return $first->id <=> $second->id;
1324          });
1325  
1326          // Consider first conversations is self-conversation.
1327          $selfconversation = array_shift($conversations);
1328          $conv1 = array_shift($conversations);
1329          $conv2 = array_shift($conversations);
1330          $conv3 = array_shift($conversations);
1331          $conv4 = array_shift($conversations);
1332  
1333          $this->assertTrue($conv1->ismuted);
1334          $this->assertFalse($conv2->ismuted);
1335          $this->assertTrue($conv3->ismuted);
1336          $this->assertFalse($conv4->ismuted);
1337      }
1338  
1339      /**
1340       * Tests retrieving conversations with a limit and offset to ensure pagination works correctly.
1341       */
1342      public function test_get_conversations_limit_offset() {
1343          // Get a bunch of conversations, some group, some individual and in different states.
1344          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
1345              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
1346  
1347          // Get all conversations for user1, limited to 1 result.
1348          $conversations = api::get_conversations($user1->id, 0, 1);
1349  
1350          // Verify the first conversation.
1351          $this->assertCount(1, $conversations);
1352          $conversation = array_shift($conversations);
1353          $this->assertEquals($conversation->id, $gc3->id);
1354  
1355          // Verify the next conversation.
1356          $conversations = api::get_conversations($user1->id, 1, 1);
1357          $this->assertCount(1, $conversations);
1358          $this->assertEquals($gc2->id, $conversations[0]->id);
1359  
1360          // Verify the next conversation.
1361          $conversations = api::get_conversations($user1->id, 2, 1);
1362          $this->assertCount(1, $conversations);
1363          $this->assertEquals($ic2->id, $conversations[0]->id);
1364  
1365          // Skip one and get both empty conversations.
1366          $conversations = api::get_conversations($user1->id, 4, 2);
1367          $this->assertCount(2, $conversations);
1368          $this->assertEquals($gc5->id, $conversations[0]->id);
1369          $this->assertEmpty($conversations[0]->messages);
1370          $this->assertEquals($gc4->id, $conversations[1]->id);
1371          $this->assertEmpty($conversations[1]->messages);
1372  
1373          // Ask for an offset that doesn't exist and verify no conversations are returned.
1374          $conversations = api::get_conversations($user1->id, 10, 1);
1375          $this->assertCount(0, $conversations);
1376      }
1377  
1378      /**
1379       * Test verifying the type filtering behaviour of the
1380       */
1381      public function test_get_conversations_type_filter() {
1382          // Get a bunch of conversations, some group, some individual and in different states.
1383          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
1384              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
1385  
1386          // Verify we can ask for only individual conversations.
1387          $conversations = api::get_conversations($user1->id, 0, 20,
1388              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL);
1389          $this->assertCount(2, $conversations);
1390  
1391          // Verify we can ask for only group conversations.
1392          $conversations = api::get_conversations($user1->id, 0, 20,
1393              api::MESSAGE_CONVERSATION_TYPE_GROUP);
1394          $this->assertCount(4, $conversations);
1395  
1396          // Verify an exception is thrown if an unrecognized type is specified.
1397          $this->expectException(\moodle_exception::class);
1398          $conversations = api::get_conversations($user1->id, 0, 20, 0);
1399      }
1400  
1401      /**
1402       * Tests retrieving conversations when a 'self' conversation exists.
1403       */
1404      public function test_get_conversations_self_conversations() {
1405          global $DB;
1406  
1407          // Create a conversation between one user and themself.
1408          $user1 = self::getDataGenerator()->create_user();
1409          $user2 = self::getDataGenerator()->create_user();
1410          $user3 = self::getDataGenerator()->create_user();
1411          $user4 = self::getDataGenerator()->create_user();
1412  
1413          // Create some individual conversations.
1414          $ic1 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1415              [$user1->id, $user2->id]);
1416          $ic2 = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1417              [$user1->id, $user3->id]);
1418          testhelper::send_fake_message_to_conversation($user1, $ic1->id, 'Message from user1 to user2');
1419  
1420          // Get some self-conversations.
1421          $sc1 = api::get_self_conversation($user1->id);
1422          $sc4 = api::get_self_conversation($user4->id);
1423          testhelper::send_fake_message_to_conversation($user1, $sc1->id, 'Test message to self 1!');
1424  
1425          // Verify we are in a 'self' conversation state.
1426          $members = $DB->get_records('message_conversation_members', ['conversationid' => $sc1->id]);
1427          $this->assertCount(1, $members);
1428          $member = array_pop($members);
1429          $this->assertEquals($user1->id, $member->userid);
1430  
1431          // Verify the self-conversations are returned by the method.
1432          $conversations = api::get_conversations($user1->id, 0, 20, api::MESSAGE_CONVERSATION_TYPE_SELF);
1433          $this->assertCount(1, $conversations);
1434          $conversation = array_pop($conversations);
1435          $this->assertEquals($conversation->id, $sc1->id);
1436  
1437          $conversations = api::get_conversations($user4->id);
1438          // The self-conversation.
1439          $this->assertCount(1, $conversations);
1440  
1441          // Get only private conversations for user1 (empty conversations, like $ic2, are not returned).
1442          $conversations = api::get_conversations($user1->id, 0, 20,
1443              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL);
1444          $this->assertCount(1, $conversations);
1445  
1446          // Merge self with private conversations for user1.
1447          $conversations = api::get_conversations($user1->id, 0, 20,
1448              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, null, true);
1449          $this->assertCount(2, $conversations);
1450  
1451          // Get only private conversations for user2.
1452          $conversations = api::get_conversations($user2->id, 0, 20,
1453              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL);
1454          $this->assertCount(1, $conversations);
1455  
1456          // Merge self with private conversations for user2.
1457          $conversations = api::get_conversations($user2->id, 0, 20,
1458              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, null, true);
1459          $this->assertCount(2, $conversations);
1460      }
1461  
1462      /**
1463       * Tests retrieving conversations when a conversation contains a deleted user.
1464       */
1465      public function test_get_conversations_with_deleted_user() {
1466          // Get a bunch of conversations, some group, some individual and in different states.
1467          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
1468              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
1469  
1470          // Delete the second user and retrieve the conversations.
1471          // We should have 6 still, as conversations with soft-deleted users are still returned.
1472          // Group conversations are also present, albeit with less members.
1473          delete_user($user2);
1474          // This is to confirm an exception is not thrown when a user AND the user context is deleted.
1475          // We no longer delete the user context, but historically we did.
1476          \context_helper::delete_instance(CONTEXT_USER, $user2->id);
1477          $conversations = api::get_conversations($user1->id);
1478          // Consider there's a self-conversation (the last one).
1479          $this->assertCount(7, $conversations);
1480          $this->assertEquals($gc3->id, $conversations[0]->id);
1481          $this->assertcount(1, $conversations[0]->members);
1482          $this->assertEquals($gc2->id, $conversations[1]->id);
1483          $this->assertcount(1, $conversations[1]->members);
1484          $this->assertEquals($ic2->id, $conversations[2]->id);
1485          $this->assertEquals($ic1->id, $conversations[3]->id);
1486          $this->assertEquals($gc5->id, $conversations[4]->id);
1487          $this->assertEquals($gc4->id, $conversations[5]->id);
1488  
1489          // Delete a user from a group conversation where that user had sent the most recent message.
1490          // This user will still be present in the members array, as will the message in the messages array.
1491          delete_user($user4);
1492          $conversations = api::get_conversations($user1->id);
1493  
1494          // Consider there's a self-conversation (the last one).
1495          $this->assertCount(7, $conversations);
1496          $this->assertEquals($gc2->id, $conversations[1]->id);
1497          $this->assertcount(1, $conversations[1]->members);
1498          $this->assertEquals($user4->id, $conversations[1]->members[$user4->id]->id);
1499          $this->assertcount(1, $conversations[1]->messages);
1500          $this->assertEquals($user4->id, $conversations[1]->messages[0]->useridfrom);
1501  
1502          // Delete the third user and retrieve the conversations.
1503          // We should have 6 still, as conversations with soft-deleted users are still returned.
1504          // Group conversations are also present, albeit with less members.
1505          delete_user($user3);
1506          $conversations = api::get_conversations($user1->id);
1507          // Consider there's a self-conversation (the last one).
1508          $this->assertCount(7, $conversations);
1509          $this->assertEquals($gc3->id, $conversations[0]->id);
1510          $this->assertcount(1, $conversations[0]->members);
1511          $this->assertEquals($gc2->id, $conversations[1]->id);
1512          $this->assertcount(1, $conversations[1]->members);
1513          $this->assertEquals($ic2->id, $conversations[2]->id);
1514          $this->assertEquals($ic1->id, $conversations[3]->id);
1515          $this->assertEquals($gc5->id, $conversations[4]->id);
1516          $this->assertEquals($gc4->id, $conversations[5]->id);
1517      }
1518  
1519      /**
1520       * Test confirming the behaviour of get_conversations() when users delete all messages.
1521       */
1522      public function test_get_conversations_deleted_messages() {
1523          // Get a bunch of conversations, some group, some individual and in different states.
1524          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
1525              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
1526  
1527          $conversations = api::get_conversations($user1->id);
1528          // Consider first conversations is self-conversation.
1529          $this->assertCount(7, $conversations);
1530  
1531          // Delete all messages from a group conversation the user is in - it should be returned.
1532          $this->assertTrue(api::is_user_in_conversation($user1->id, $gc2->id));
1533          $convmessages = api::get_conversation_messages($user1->id, $gc2->id);
1534          $messages = $convmessages['messages'];
1535          foreach ($messages as $message) {
1536              api::delete_message($user1->id, $message->id);
1537          }
1538          $conversations = api::get_conversations($user1->id);
1539          // Consider first conversations is self-conversation.
1540          $this->assertCount(7, $conversations);
1541          $this->assertContainsEquals($gc2->id, array_column($conversations, 'id'));
1542  
1543          // Delete all messages from an individual conversation the user is in - it should not be returned.
1544          $this->assertTrue(api::is_user_in_conversation($user1->id, $ic1->id));
1545          $convmessages = api::get_conversation_messages($user1->id, $ic1->id);
1546          $messages = $convmessages['messages'];
1547          foreach ($messages as $message) {
1548              api::delete_message($user1->id, $message->id);
1549          }
1550          $conversations = api::get_conversations($user1->id);
1551          // Consider first conversations is self-conversation.
1552          $this->assertCount(6, $conversations);
1553          $this->assertNotContainsEquals($ic1->id, array_column($conversations, 'id'));
1554      }
1555  
1556      /**
1557       * Test verifying the behaviour of get_conversations() when fetching favourite conversations with only a single
1558       * favourite.
1559       */
1560      public function test_get_conversations_favourite_conversations_single() {
1561          // Get a bunch of conversations, some group, some individual and in different states.
1562          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
1563              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
1564  
1565          // Mark a single conversation as favourites.
1566          api::set_favourite_conversation($ic2->id, $user1->id);
1567  
1568          // Get the conversation, first with no restrictions, confirming the favourite status of the conversations.
1569          $conversations = api::get_conversations($user1->id);
1570          // Consider there is a self-conversation.
1571          $selfconversation = api::get_self_conversation($user1->id);
1572          $this->assertCount(7, $conversations);
1573          foreach ($conversations as $conv) {
1574              if (in_array($conv->id, [$ic2->id, $selfconversation->id])) {
1575                  $this->assertTrue($conv->isfavourite);
1576              } else {
1577                  $this->assertFalse($conv->isfavourite);
1578              }
1579          }
1580  
1581          // Now, get ONLY favourite conversations (including self-conversation).
1582          $conversations = api::get_conversations($user1->id, 0, 20, null, true);
1583          $this->assertCount(2, $conversations);
1584          foreach ($conversations as $conv) {
1585              if ($conv->type != api::MESSAGE_CONVERSATION_TYPE_SELF) {
1586                  $this->assertTrue($conv->isfavourite);
1587                  $this->assertEquals(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $conv->type);
1588                  $this->assertEquals($ic2->id, $conv->id);
1589              }
1590          }
1591  
1592          // Now, try ONLY favourites of type 'group'.
1593          $conversations = api::get_conversations($user1->id, 0, 20,
1594              api::MESSAGE_CONVERSATION_TYPE_GROUP, true);
1595          $this->assertEmpty($conversations);
1596  
1597          // And NO favourite conversations.
1598          $conversations = api::get_conversations($user1->id, 0, 20, null, false);
1599          $this->assertCount(5, $conversations);
1600          foreach ($conversations as $conv) {
1601              $this->assertFalse($conv->isfavourite);
1602              $this->assertNotEquals($ic2, $conv->id);
1603          }
1604      }
1605  
1606      /**
1607       * Test verifying the behaviour of get_conversations() when fetching favourite conversations.
1608       */
1609      public function test_get_conversations_favourite_conversations() {
1610          // Get a bunch of conversations, some group, some individual and in different states.
1611          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
1612              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
1613  
1614          // Try to get ONLY favourite conversations, when only self-conversation exist.
1615          $this->assertCount(1, api::get_conversations($user1->id, 0, 20, null, true));
1616  
1617          // Unstar self-conversation.
1618          $selfconversation = api::get_self_conversation($user1->id);
1619          api::unset_favourite_conversation($selfconversation->id, $user1->id);
1620  
1621          // Try to get ONLY favourite conversations, when no favourites exist.
1622          $this->assertEquals([], api::get_conversations($user1->id, 0, 20, null, true));
1623  
1624          // Try to get NO favourite conversations, when no favourites exist.
1625          $this->assertCount(7, api::get_conversations($user1->id, 0, 20, null, false));
1626  
1627          // Mark a few conversations as favourites.
1628          api::set_favourite_conversation($ic1->id, $user1->id);
1629          api::set_favourite_conversation($gc2->id, $user1->id);
1630          api::set_favourite_conversation($gc5->id, $user1->id);
1631          $favouriteids = [$ic1->id, $gc2->id, $gc5->id];
1632  
1633          // Get the conversations, first with no restrictions, confirming the favourite status of the conversations.
1634          $conversations = api::get_conversations($user1->id);
1635          $this->assertCount(7, $conversations);
1636          foreach ($conversations as $conv) {
1637              if (in_array($conv->id, $favouriteids)) {
1638                  $this->assertTrue($conv->isfavourite);
1639              } else {
1640                  $this->assertFalse($conv->isfavourite);
1641              }
1642          }
1643  
1644          // Now, get ONLY favourite conversations.
1645          $conversations = api::get_conversations($user1->id, 0, 20, null, true);
1646          $this->assertCount(3, $conversations);
1647          foreach ($conversations as $conv) {
1648              $this->assertTrue($conv->isfavourite);
1649              $this->assertNotFalse(array_search($conv->id, $favouriteids));
1650          }
1651  
1652          // Now, try ONLY favourites of type 'group'.
1653          $conversations = api::get_conversations($user1->id, 0, 20,
1654              api::MESSAGE_CONVERSATION_TYPE_GROUP, true);
1655          $this->assertCount(2, $conversations);
1656          foreach ($conversations as $conv) {
1657              $this->assertTrue($conv->isfavourite);
1658              $this->assertNotFalse(array_search($conv->id, [$gc2->id, $gc5->id]));
1659          }
1660  
1661          // And NO favourite conversations.
1662          $conversations = api::get_conversations($user1->id, 0, 20, null, false);
1663          $this->assertCount(4, $conversations);
1664          foreach ($conversations as $conv) {
1665              $this->assertFalse($conv->isfavourite);
1666              $this->assertFalse(array_search($conv->id, $favouriteids));
1667          }
1668      }
1669  
1670      /**
1671       * Test verifying get_conversations when there are users in a group and/or individual conversation. The reason this
1672       * test is performed is because we do not need as much data for group conversations (saving DB calls), so we want
1673       * to confirm this happens.
1674       */
1675      public function test_get_conversations_user_in_group_and_individual_chat() {
1676          $this->resetAfterTest();
1677  
1678          $user1 = self::getDataGenerator()->create_user();
1679          $user2 = self::getDataGenerator()->create_user();
1680          $user3 = self::getDataGenerator()->create_user();
1681  
1682          $conversation = api::create_conversation(
1683              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1684              [
1685                  $user1->id,
1686                  $user2->id
1687              ],
1688              'Individual conversation'
1689          );
1690  
1691          testhelper::send_fake_message_to_conversation($user1, $conversation->id);
1692  
1693          $conversation = api::create_conversation(
1694              api::MESSAGE_CONVERSATION_TYPE_GROUP,
1695              [
1696                  $user1->id,
1697                  $user2->id,
1698              ],
1699              'Group conversation'
1700          );
1701  
1702          testhelper::send_fake_message_to_conversation($user1, $conversation->id);
1703  
1704          api::create_contact_request($user1->id, $user2->id);
1705          api::create_contact_request($user1->id, $user3->id);
1706  
1707          $conversations = api::get_conversations($user2->id);
1708  
1709          $groupconversation = array_shift($conversations);
1710          $individualconversation = array_shift($conversations);
1711  
1712          $this->assertEquals('Group conversation', $groupconversation->name);
1713          $this->assertEquals('Individual conversation', $individualconversation->name);
1714  
1715          $this->assertCount(1, $groupconversation->members);
1716          $this->assertCount(1, $individualconversation->members);
1717  
1718          $groupmember = reset($groupconversation->members);
1719          $this->assertNull($groupmember->requirescontact);
1720          $this->assertNull($groupmember->canmessage);
1721          $this->assertEmpty($groupmember->contactrequests);
1722  
1723          $individualmember = reset($individualconversation->members);
1724          $this->assertNotNull($individualmember->requirescontact);
1725          $this->assertNotNull($individualmember->canmessage);
1726          $this->assertNotEmpty($individualmember->contactrequests);
1727      }
1728  
1729      /**
1730       * Test verifying that group linked conversations are returned and contain a subname matching the course name.
1731       */
1732      public function test_get_conversations_group_linked() {
1733          global $CFG, $DB;
1734  
1735          // Create some users.
1736          $user1 = self::getDataGenerator()->create_user();
1737          $user2 = self::getDataGenerator()->create_user();
1738          $user3 = self::getDataGenerator()->create_user();
1739  
1740          $course1 = $this->getDataGenerator()->create_course();
1741  
1742          // Create a group with a linked conversation and a valid image.
1743          $this->setAdminUser();
1744          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
1745          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
1746          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
1747          $group1 = $this->getDataGenerator()->create_group([
1748              'courseid' => $course1->id,
1749              'enablemessaging' => 1,
1750              'picturepath' => $CFG->dirroot . '/lib/tests/fixtures/gd-logo.png'
1751          ]);
1752  
1753          // Add users to group1.
1754          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
1755          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
1756  
1757          // Verify the group with the image works as expected.
1758          $conversations = api::get_conversations($user1->id);
1759          $this->assertEquals(2, $conversations[0]->membercount);
1760          $this->assertEquals($course1->shortname, $conversations[0]->subname);
1761          $groupimageurl = get_group_picture_url($group1, $group1->courseid, true);
1762          $this->assertEquals($groupimageurl, $conversations[0]->imageurl);
1763  
1764          // Create a group with a linked conversation and without any image.
1765          $group2 = $this->getDataGenerator()->create_group([
1766              'courseid' => $course1->id,
1767              'enablemessaging' => 1,
1768          ]);
1769  
1770          // Add users to group2.
1771          $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id));
1772          $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user3->id));
1773  
1774          // Verify the group without any image works as expected too.
1775          $conversations = api::get_conversations($user3->id);
1776          // Consider first conversations is self-conversation.
1777          $this->assertEquals(2, $conversations[0]->membercount);
1778          $this->assertEquals($course1->shortname, $conversations[0]->subname);
1779          $this->assertEquals('https://www.example.com/moodle/theme/image.php/_s/boost/core/1/g/g1', $conversations[0]->imageurl);
1780  
1781          // Now, disable the conversation linked to the group and verify it's no longer returned.
1782          $DB->set_field('message_conversations', 'enabled', 0, ['id' => $conversations[0]->id]);
1783          $conversations = api::get_conversations($user3->id);
1784          $this->assertCount(1, $conversations);
1785      }
1786  
1787     /**
1788      * The data provider for get_conversations_mixed.
1789      *
1790      * This provides sets of data to for testing.
1791      * @return array
1792      */
1793     public function get_conversations_mixed_provider() {
1794         return array(
1795              'Test that conversations with messages contacts is correctly ordered.' => array(
1796                  'users' => array(
1797                      'user1',
1798                      'user2',
1799                      'user3',
1800                  ),
1801                  'contacts' => array(
1802                  ),
1803                  'messages' => array(
1804                      array(
1805                          'from'          => 'user1',
1806                          'to'            => 'user2',
1807                          'state'         => 'unread',
1808                          'subject'       => 'S1',
1809                      ),
1810                      array(
1811                          'from'          => 'user2',
1812                          'to'            => 'user1',
1813                          'state'         => 'unread',
1814                          'subject'       => 'S2',
1815                      ),
1816                      array(
1817                          'from'          => 'user1',
1818                          'to'            => 'user2',
1819                          'state'         => 'unread',
1820                          'timecreated'   => 0,
1821                          'subject'       => 'S3',
1822                      ),
1823                      array(
1824                          'from'          => 'user1',
1825                          'to'            => 'user3',
1826                          'state'         => 'read',
1827                          'timemodifier'  => 1,
1828                          'subject'       => 'S4',
1829                      ),
1830                      array(
1831                          'from'          => 'user3',
1832                          'to'            => 'user1',
1833                          'state'         => 'read',
1834                          'timemodifier'  => 1,
1835                          'subject'       => 'S5',
1836                      ),
1837                      array(
1838                          'from'          => 'user1',
1839                          'to'            => 'user3',
1840                          'state'         => 'read',
1841                          'timecreated'   => 0,
1842                          'subject'       => 'S6',
1843                      ),
1844                  ),
1845                  'expectations' => array(
1846                      'user1' => array(
1847                          // User1 has conversed most recently with user3. The most recent message is M5.
1848                          array(
1849                              'messageposition'   => 0,
1850                              'with'              => 'user3',
1851                              'subject'           => '<p>S5</p>',
1852                              'unreadcount'       => 0,
1853                          ),
1854                          // User1 has also conversed with user2. The most recent message is S2.
1855                          array(
1856                              'messageposition'   => 1,
1857                              'with'              => 'user2',
1858                              'subject'           => '<p>S2</p>',
1859                              'unreadcount'       => 1,
1860                          ),
1861                      ),
1862                      'user2' => array(
1863                          // User2 has only conversed with user1. Their most recent shared message was S2.
1864                          array(
1865                              'messageposition'   => 0,
1866                              'with'              => 'user1',
1867                              'subject'           => '<p>S2</p>',
1868                              'unreadcount'       => 2,
1869                          ),
1870                      ),
1871                      'user3' => array(
1872                          // User3 has only conversed with user1. Their most recent shared message was S5.
1873                          array(
1874                              'messageposition'   => 0,
1875                              'with'              => 'user1',
1876                              'subject'           => '<p>S5</p>',
1877                              'unreadcount'       => 0,
1878                          ),
1879                      ),
1880                  ),
1881              ),
1882              'Test conversations with a single user, where some messages are read and some are not.' => array(
1883                  'users' => array(
1884                      'user1',
1885                      'user2',
1886                  ),
1887                  'contacts' => array(
1888                  ),
1889                  'messages' => array(
1890                      array(
1891                          'from'          => 'user1',
1892                          'to'            => 'user2',
1893                          'state'         => 'read',
1894                          'subject'       => 'S1',
1895                      ),
1896                      array(
1897                          'from'          => 'user2',
1898                          'to'            => 'user1',
1899                          'state'         => 'read',
1900                          'subject'       => 'S2',
1901                      ),
1902                      array(
1903                          'from'          => 'user1',
1904                          'to'            => 'user2',
1905                          'state'         => 'unread',
1906                          'timemodifier'  => 1,
1907                          'subject'       => 'S3',
1908                      ),
1909                      array(
1910                          'from'          => 'user1',
1911                          'to'            => 'user2',
1912                          'state'         => 'unread',
1913                          'timemodifier'  => 1,
1914                          'subject'       => 'S4',
1915                      ),
1916                  ),
1917                  'expectations' => array(
1918                      // The most recent message between user1 and user2 was S4.
1919                      'user1' => array(
1920                          array(
1921                              'messageposition'   => 0,
1922                              'with'              => 'user2',
1923                              'subject'           => '<p>S4</p>',
1924                              'unreadcount'       => 0,
1925                          ),
1926                      ),
1927                      'user2' => array(
1928                          // The most recent message between user1 and user2 was S4.
1929                          array(
1930                              'messageposition'   => 0,
1931                              'with'              => 'user1',
1932                              'subject'           => '<p>S4</p>',
1933                              'unreadcount'       => 2,
1934                          ),
1935                      ),
1936                  ),
1937              ),
1938              'Test conversations with a single user, where some messages are read and some are not, and messages ' .
1939              'are out of order' => array(
1940              // This can happen through a combination of factors including multi-master DB replication with messages
1941              // read somehow (e.g. API).
1942                  'users' => array(
1943                      'user1',
1944                      'user2',
1945                  ),
1946                  'contacts' => array(
1947                  ),
1948                  'messages' => array(
1949                      array(
1950                          'from'          => 'user1',
1951                          'to'            => 'user2',
1952                          'state'         => 'read',
1953                          'subject'       => 'S1',
1954                          'timemodifier'  => 1,
1955                      ),
1956                      array(
1957                          'from'          => 'user2',
1958                          'to'            => 'user1',
1959                          'state'         => 'read',
1960                          'subject'       => 'S2',
1961                          'timemodifier'  => 2,
1962                      ),
1963                      array(
1964                          'from'          => 'user1',
1965                          'to'            => 'user2',
1966                          'state'         => 'unread',
1967                          'subject'       => 'S3',
1968                      ),
1969                      array(
1970                          'from'          => 'user1',
1971                          'to'            => 'user2',
1972                          'state'         => 'unread',
1973                          'subject'       => 'S4',
1974                      ),
1975                  ),
1976                  'expectations' => array(
1977                      // The most recent message between user1 and user2 was S2, even though later IDs have not been read.
1978                      'user1' => array(
1979                          array(
1980                              'messageposition'   => 0,
1981                              'with'              => 'user2',
1982                              'subject'           => '<p>S2</p>',
1983                              'unreadcount'       => 0,
1984                          ),
1985                      ),
1986                      'user2' => array(
1987                          array(
1988                              'messageposition'   => 0,
1989                              'with'              => 'user1',
1990                              'subject'           => '<p>S2</p>',
1991                              'unreadcount'       => 2
1992                          ),
1993                      ),
1994                  ),
1995              ),
1996              'Test unread message count is correct for both users' => array(
1997                  'users' => array(
1998                      'user1',
1999                      'user2',
2000                  ),
2001                  'contacts' => array(
2002                  ),
2003                  'messages' => array(
2004                      array(
2005                          'from'          => 'user1',
2006                          'to'            => 'user2',
2007                          'state'         => 'read',
2008                          'subject'       => 'S1',
2009                          'timemodifier'  => 1,
2010                      ),
2011                      array(
2012                          'from'          => 'user2',
2013                          'to'            => 'user1',
2014                          'state'         => 'read',
2015                          'subject'       => 'S2',
2016                          'timemodifier'  => 2,
2017                      ),
2018                      array(
2019                          'from'          => 'user1',
2020                          'to'            => 'user2',
2021                          'state'         => 'read',
2022                          'subject'       => 'S3',
2023                          'timemodifier'  => 3,
2024                      ),
2025                      array(
2026                          'from'          => 'user1',
2027                          'to'            => 'user2',
2028                          'state'         => 'read',
2029                          'subject'       => 'S4',
2030                          'timemodifier'  => 4,
2031                      ),
2032                      array(
2033                          'from'          => 'user1',
2034                          'to'            => 'user2',
2035                          'state'         => 'unread',
2036                          'subject'       => 'S5',
2037                          'timemodifier'  => 5,
2038                      ),
2039                      array(
2040                          'from'          => 'user2',
2041                          'to'            => 'user1',
2042                          'state'         => 'unread',
2043                          'subject'       => 'S6',
2044                          'timemodifier'  => 6,
2045                      ),
2046                      array(
2047                          'from'          => 'user1',
2048                          'to'            => 'user2',
2049                          'state'         => 'unread',
2050                          'subject'       => 'S7',
2051                          'timemodifier'  => 7,
2052                      ),
2053                      array(
2054                          'from'          => 'user1',
2055                          'to'            => 'user2',
2056                          'state'         => 'unread',
2057                          'subject'       => 'S8',
2058                          'timemodifier'  => 8,
2059                      ),
2060                  ),
2061                  'expectations' => array(
2062                      // The most recent message between user1 and user2 was S2, even though later IDs have not been read.
2063                      'user1' => array(
2064                          array(
2065                              'messageposition'   => 0,
2066                              'with'              => 'user2',
2067                              'subject'           => '<p>S8</p>',
2068                              'unreadcount'       => 1,
2069                          ),
2070                      ),
2071                      'user2' => array(
2072                          array(
2073                              'messageposition'   => 0,
2074                              'with'              => 'user1',
2075                              'subject'           => '<p>S8</p>',
2076                              'unreadcount'       => 3,
2077                          ),
2078                      ),
2079                  ),
2080              ),
2081          );
2082      }
2083  
2084      /**
2085       * Test that creation can't create the same conversation twice for 1:1 conversations.
2086       */
2087      public function test_create_conversation_duplicate_conversations() {
2088          global $DB;
2089          $user1 = $this::getDataGenerator()->create_user();
2090  
2091          api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_SELF, [$user1->id]);
2092          api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_SELF, [$user1->id]);
2093  
2094          $convhash = helper::get_conversation_hash([$user1->id]);
2095          $countconversations = $DB->count_records('message_conversations', ['convhash' => $convhash]);
2096          $this->assertEquals(1, $countconversations);
2097          $this->assertNotEmpty($conversation = api::get_self_conversation($user1->id));
2098      }
2099  
2100      /**
2101       * Test get_conversations with a mixture of messages.
2102       *
2103       * @dataProvider get_conversations_mixed_provider
2104       * @param array $usersdata The list of users to create for this test.
2105       * @param array $messagesdata The list of messages to create.
2106       * @param array $expectations The list of expected outcomes.
2107       */
2108      public function test_get_conversations_mixed($usersdata, $contacts, $messagesdata, $expectations) {
2109          global $DB;
2110  
2111          // Create all of the users.
2112          $users = array();
2113          foreach ($usersdata as $username) {
2114              $users[$username] = $this->getDataGenerator()->create_user(array('username' => $username));
2115          }
2116  
2117          foreach ($contacts as $username => $contact) {
2118              foreach ($contact as $contactname => $blocked) {
2119                  $record = new \stdClass();
2120                  $record->userid     = $users[$username]->id;
2121                  $record->contactid  = $users[$contactname]->id;
2122                  $record->blocked    = $blocked;
2123                  $record->id = $DB->insert_record('message_contacts', $record);
2124              }
2125          }
2126  
2127          $defaulttimecreated = time();
2128          foreach ($messagesdata as $messagedata) {
2129              $from       = $users[$messagedata['from']];
2130              $to         = $users[$messagedata['to']];
2131              $subject    = $messagedata['subject'];
2132  
2133              if (isset($messagedata['state']) && $messagedata['state'] == 'unread') {
2134                  $messageid = $this->send_fake_message($from, $to, $subject);
2135              } else {
2136                  // If there is no state, or the state is not 'unread', assume the message is read.
2137                  $messageid = message_post_message($from, $to, $subject, FORMAT_PLAIN);
2138              }
2139  
2140              $updatemessage = new \stdClass();
2141              $updatemessage->id = $messageid;
2142              if (isset($messagedata['timecreated'])) {
2143                  $updatemessage->timecreated = $messagedata['timecreated'];
2144              } else if (isset($messagedata['timemodifier'])) {
2145                  $updatemessage->timecreated = $defaulttimecreated + $messagedata['timemodifier'];
2146              } else {
2147                  $updatemessage->timecreated = $defaulttimecreated;
2148              }
2149  
2150              $DB->update_record('messages', $updatemessage);
2151          }
2152  
2153          foreach ($expectations as $username => $data) {
2154              // Get the recent conversations for the specified user.
2155              $user = $users[$username];
2156              $conversations = array_values(api::get_conversations($user->id));
2157              foreach ($data as $expectation) {
2158                  $otheruser = $users[$expectation['with']];
2159                  $conversation = $conversations[$expectation['messageposition']];
2160                  $this->assertEquals($otheruser->id, $conversation->members[$otheruser->id]->id);
2161                  $this->assertEquals($expectation['subject'], $conversation->messages[0]->text);
2162                  $this->assertEquals($expectation['unreadcount'], $conversation->unreadcount);
2163              }
2164          }
2165      }
2166  
2167      /**
2168       * Tests retrieving user contacts.
2169       */
2170      public function test_get_user_contacts() {
2171          // Create some users.
2172          $user1 = self::getDataGenerator()->create_user();
2173  
2174          // Set as the user.
2175          $this->setUser($user1);
2176  
2177          $user2 = new \stdClass();
2178          $user2->firstname = 'User';
2179          $user2->lastname = 'A';
2180          $user2 = self::getDataGenerator()->create_user($user2);
2181  
2182          $user3 = new \stdClass();
2183          $user3->firstname = 'User';
2184          $user3->lastname = 'B';
2185          $user3 = self::getDataGenerator()->create_user($user3);
2186  
2187          $user4 = new \stdClass();
2188          $user4->firstname = 'User';
2189          $user4->lastname = 'C';
2190          $user4 = self::getDataGenerator()->create_user($user4);
2191  
2192          $user5 = new \stdClass();
2193          $user5->firstname = 'User';
2194          $user5->lastname = 'D';
2195          $user5 = self::getDataGenerator()->create_user($user5);
2196  
2197          // Add some users as contacts.
2198          api::add_contact($user1->id, $user2->id);
2199          api::add_contact($user1->id, $user3->id);
2200          api::add_contact($user1->id, $user4->id);
2201  
2202          // Retrieve the contacts.
2203          $contacts = api::get_user_contacts($user1->id);
2204  
2205          // Confirm the data is correct.
2206          $this->assertEquals(3, count($contacts));
2207  
2208          ksort($contacts);
2209  
2210          $contact1 = array_shift($contacts);
2211          $contact2 = array_shift($contacts);
2212          $contact3 = array_shift($contacts);
2213  
2214          $this->assertEquals($user2->id, $contact1->id);
2215          $this->assertEquals(fullname($user2), $contact1->fullname);
2216          $this->assertTrue($contact1->iscontact);
2217  
2218          $this->assertEquals($user3->id, $contact2->id);
2219          $this->assertEquals(fullname($user3), $contact2->fullname);
2220          $this->assertTrue($contact2->iscontact);
2221  
2222          $this->assertEquals($user4->id, $contact3->id);
2223          $this->assertEquals(fullname($user4), $contact3->fullname);
2224          $this->assertTrue($contact3->iscontact);
2225      }
2226  
2227      /**
2228       * Tests retrieving conversation messages.
2229       */
2230      public function test_get_conversation_messages() {
2231          // Create some users.
2232          $user1 = self::getDataGenerator()->create_user();
2233          $user2 = self::getDataGenerator()->create_user();
2234  
2235          // Create conversation.
2236          $conversation = api::create_conversation(
2237              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2238              [$user1->id, $user2->id]
2239          );
2240  
2241          // The person doing the search.
2242          $this->setUser($user1);
2243  
2244          // Send some messages back and forth.
2245          $time = 1;
2246          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time + 1);
2247          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Sup mang?', $time + 2);
2248          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Writing PHPUnit tests!', $time + 3);
2249          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 4);
2250  
2251          // Retrieve the messages.
2252          $convmessages = api::get_conversation_messages($user1->id, $conversation->id);
2253  
2254          // Confirm the conversation id is correct.
2255          $this->assertEquals($conversation->id, $convmessages['id']);
2256  
2257          // Confirm the message data is correct.
2258          $messages = $convmessages['messages'];
2259          $this->assertEquals(4, count($messages));
2260          $message1 = $messages[0];
2261          $message2 = $messages[1];
2262          $message3 = $messages[2];
2263          $message4 = $messages[3];
2264  
2265          $this->assertEquals($user1->id, $message1->useridfrom);
2266          $this->assertStringContainsString('Yo!', $message1->text);
2267  
2268          $this->assertEquals($user2->id, $message2->useridfrom);
2269          $this->assertStringContainsString('Sup mang?', $message2->text);
2270  
2271          $this->assertEquals($user1->id, $message3->useridfrom);
2272          $this->assertStringContainsString('Writing PHPUnit tests!', $message3->text);
2273  
2274          $this->assertEquals($user1->id, $message4->useridfrom);
2275          $this->assertStringContainsString('Word.', $message4->text);
2276  
2277          // Confirm the members data is correct.
2278          $members = $convmessages['members'];
2279          $this->assertEquals(2, count($members));
2280      }
2281  
2282      /**
2283       * Tests retrieving group conversation messages.
2284       */
2285      public function test_get_group_conversation_messages() {
2286          // Create some users.
2287          $user1 = self::getDataGenerator()->create_user();
2288          $user2 = self::getDataGenerator()->create_user();
2289          $user3 = self::getDataGenerator()->create_user();
2290          $user4 = self::getDataGenerator()->create_user();
2291  
2292          // Create group conversation.
2293          $conversation = api::create_conversation(
2294              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2295              [$user1->id, $user2->id, $user3->id, $user4->id]
2296          );
2297  
2298          // The person doing the search.
2299          $this->setUser($user1);
2300  
2301          // Send some messages back and forth.
2302          $time = 1;
2303          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time + 1);
2304          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Sup mang?', $time + 2);
2305          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Writing PHPUnit tests!', $time + 3);
2306          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 4);
2307          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Yeah!', $time + 5);
2308  
2309          // Retrieve the messages.
2310          $convmessages = api::get_conversation_messages($user1->id, $conversation->id);
2311  
2312          // Confirm the conversation id is correct.
2313          $this->assertEquals($conversation->id, $convmessages['id']);
2314  
2315          // Confirm the message data is correct.
2316          $messages = $convmessages['messages'];
2317          $this->assertEquals(5, count($messages));
2318  
2319          $message1 = $messages[0];
2320          $message2 = $messages[1];
2321          $message3 = $messages[2];
2322          $message4 = $messages[3];
2323          $message5 = $messages[4];
2324  
2325          $this->assertEquals($user1->id, $message1->useridfrom);
2326          $this->assertStringContainsString('Yo!', $message1->text);
2327  
2328          $this->assertEquals($user2->id, $message2->useridfrom);
2329          $this->assertStringContainsString('Sup mang?', $message2->text);
2330  
2331          $this->assertEquals($user3->id, $message3->useridfrom);
2332          $this->assertStringContainsString('Writing PHPUnit tests!', $message3->text);
2333  
2334          $this->assertEquals($user1->id, $message4->useridfrom);
2335          $this->assertStringContainsString('Word.', $message4->text);
2336  
2337          $this->assertEquals($user2->id, $message5->useridfrom);
2338          $this->assertStringContainsString('Yeah!', $message5->text);
2339  
2340          // Confirm the members data is correct.
2341          $members = $convmessages['members'];
2342          $this->assertEquals(3, count($members));
2343      }
2344  
2345      /**
2346       * Test verifying the sorting param for get_conversation_messages is respected().
2347       */
2348      public function test_get_conversation_messages_sorting() {
2349          // Create some users.
2350          $user1 = self::getDataGenerator()->create_user();
2351          $user2 = self::getDataGenerator()->create_user();
2352          $user3 = self::getDataGenerator()->create_user();
2353  
2354          // Create conversations - 1 group and 1 individual.
2355          $conversation = api::create_conversation(
2356              api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2357              [$user1->id, $user2->id]
2358          );
2359          $conversation2 = api::create_conversation(
2360              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2361              [$user1->id, $user2->id, $user3->id]
2362          );
2363  
2364          // Send some messages back and forth.
2365          $time = 1;
2366          $m1id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time + 1);
2367          $m2id = testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Sup mang?', $time + 2);
2368          $m3id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Writing PHPUnit tests!', $time + 3);
2369          $m4id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 4);
2370  
2371          $gm1id = testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'Yo!', $time + 1);
2372          $gm2id = testhelper::send_fake_message_to_conversation($user2, $conversation2->id, 'Sup mang?', $time + 2);
2373          $gm3id = testhelper::send_fake_message_to_conversation($user3, $conversation2->id, 'Writing PHPUnit tests!', $time + 3);
2374          $gm4id = testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'Word.', $time + 4);
2375  
2376          // The person doing the search.
2377          $this->setUser($user1);
2378  
2379          // Retrieve the messages using default sort ('timecreated ASC') and verify ordering.
2380          $convmessages = api::get_conversation_messages($user1->id, $conversation->id);
2381          $messages = $convmessages['messages'];
2382          $this->assertEquals($m1id, $messages[0]->id);
2383          $this->assertEquals($m2id, $messages[1]->id);
2384          $this->assertEquals($m3id, $messages[2]->id);
2385          $this->assertEquals($m4id, $messages[3]->id);
2386  
2387          // Retrieve the messages without specifying DESC sort ordering, and verify ordering.
2388          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 0, 0, 'timecreated DESC');
2389          $messages = $convmessages['messages'];
2390          $this->assertEquals($m1id, $messages[3]->id);
2391          $this->assertEquals($m2id, $messages[2]->id);
2392          $this->assertEquals($m3id, $messages[1]->id);
2393          $this->assertEquals($m4id, $messages[0]->id);
2394  
2395          // Retrieve the messages using default sort ('timecreated ASC') and verify ordering.
2396          $convmessages = api::get_conversation_messages($user1->id, $conversation2->id);
2397          $messages = $convmessages['messages'];
2398          $this->assertEquals($gm1id, $messages[0]->id);
2399          $this->assertEquals($gm2id, $messages[1]->id);
2400          $this->assertEquals($gm3id, $messages[2]->id);
2401          $this->assertEquals($gm4id, $messages[3]->id);
2402  
2403          // Retrieve the messages without specifying DESC sort ordering, and verify ordering.
2404          $convmessages = api::get_conversation_messages($user1->id, $conversation2->id, 0, 0, 'timecreated DESC');
2405          $messages = $convmessages['messages'];
2406          $this->assertEquals($gm1id, $messages[3]->id);
2407          $this->assertEquals($gm2id, $messages[2]->id);
2408          $this->assertEquals($gm3id, $messages[1]->id);
2409          $this->assertEquals($gm4id, $messages[0]->id);
2410      }
2411  
2412      /**
2413       * Test retrieving conversation messages by providing a minimum timecreated value.
2414       */
2415      public function test_get_conversation_messages_time_from_only() {
2416          // Create some users.
2417          $user1 = self::getDataGenerator()->create_user();
2418          $user2 = self::getDataGenerator()->create_user();
2419          $user3 = self::getDataGenerator()->create_user();
2420          $user4 = self::getDataGenerator()->create_user();
2421  
2422          // Create group conversation.
2423          $conversation = api::create_conversation(
2424              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2425              [$user1->id, $user2->id, $user3->id, $user4->id]
2426          );
2427  
2428          // The person doing the search.
2429          $this->setUser($user1);
2430  
2431          // Send some messages back and forth.
2432          $time = 1;
2433          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1);
2434          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2);
2435          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3);
2436          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4);
2437  
2438          // Retrieve the messages from $time, which should be all of them.
2439          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 0, 0, 'timecreated ASC', $time);
2440  
2441          // Confirm the conversation id is correct.
2442          $this->assertEquals($conversation->id, $convmessages['id']);
2443  
2444          // Confirm the message data is correct.
2445          $messages = $convmessages['messages'];
2446          $this->assertEquals(4, count($messages));
2447  
2448          $message1 = $messages[0];
2449          $message2 = $messages[1];
2450          $message3 = $messages[2];
2451          $message4 = $messages[3];
2452  
2453          $this->assertStringContainsString('Message 1', $message1->text);
2454          $this->assertStringContainsString('Message 2', $message2->text);
2455          $this->assertStringContainsString('Message 3', $message3->text);
2456          $this->assertStringContainsString('Message 4', $message4->text);
2457  
2458          // Confirm the members data is correct.
2459          $members = $convmessages['members'];
2460          $this->assertEquals(3, count($members));
2461  
2462          // Retrieve the messages from $time + 3, which should only be the 2 last messages.
2463          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 0, 0,
2464              'timecreated ASC', $time + 3);
2465  
2466          // Confirm the conversation id is correct.
2467          $this->assertEquals($conversation->id, $convmessages['id']);
2468  
2469          // Confirm the message data is correct.
2470          $messages = $convmessages['messages'];
2471          $this->assertEquals(2, count($messages));
2472  
2473          $message1 = $messages[0];
2474          $message2 = $messages[1];
2475  
2476          $this->assertStringContainsString('Message 3', $message1->text);
2477          $this->assertStringContainsString('Message 4', $message2->text);
2478  
2479          // Confirm the members data is correct.
2480          $members = $convmessages['members'];
2481          $this->assertEquals(2, count($members));
2482      }
2483  
2484      /**
2485       * Test retrieving conversation messages by providing a maximum timecreated value.
2486       */
2487      public function test_get_conversation_messages_time_to_only() {
2488          // Create some users.
2489          $user1 = self::getDataGenerator()->create_user();
2490          $user2 = self::getDataGenerator()->create_user();
2491          $user3 = self::getDataGenerator()->create_user();
2492          $user4 = self::getDataGenerator()->create_user();
2493  
2494          // Create group conversation.
2495          $conversation = api::create_conversation(
2496              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2497              [$user1->id, $user2->id, $user3->id, $user4->id]
2498          );
2499  
2500          // The person doing the search.
2501          $this->setUser($user1);
2502  
2503          // Send some messages back and forth.
2504          $time = 1;
2505          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1);
2506          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2);
2507          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3);
2508          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4);
2509  
2510          // Retrieve the messages up until $time + 4, which should be all of them.
2511          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 0, 0, 'timecreated ASC',
2512              0, $time + 4);
2513  
2514          // Confirm the conversation id is correct.
2515          $this->assertEquals($conversation->id, $convmessages['id']);
2516  
2517          // Confirm the message data is correct.
2518          $messages = $convmessages['messages'];
2519          $this->assertEquals(4, count($messages));
2520  
2521          $message1 = $messages[0];
2522          $message2 = $messages[1];
2523          $message3 = $messages[2];
2524          $message4 = $messages[3];
2525  
2526          $this->assertStringContainsString('Message 1', $message1->text);
2527          $this->assertStringContainsString('Message 2', $message2->text);
2528          $this->assertStringContainsString('Message 3', $message3->text);
2529          $this->assertStringContainsString('Message 4', $message4->text);
2530  
2531          // Confirm the members data is correct.
2532          $members = $convmessages['members'];
2533          $this->assertEquals(3, count($members));
2534  
2535          // Retrieve the messages up until $time + 2, which should be the first two.
2536          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 0, 0, 'timecreated ASC',
2537              0, $time + 2);
2538  
2539          // Confirm the conversation id is correct.
2540          $this->assertEquals($conversation->id, $convmessages['id']);
2541  
2542          // Confirm the message data is correct.
2543          $messages = $convmessages['messages'];
2544          $this->assertEquals(2, count($messages));
2545  
2546          $message1 = $messages[0];
2547          $message2 = $messages[1];
2548  
2549          $this->assertStringContainsString('Message 1', $message1->text);
2550          $this->assertStringContainsString('Message 2', $message2->text);
2551  
2552          // Confirm the members data is correct.
2553          $members = $convmessages['members'];
2554          $this->assertEquals(2, count($members));
2555      }
2556  
2557      /**
2558       * Test retrieving conversation messages by providing a minimum and maximum timecreated value.
2559       */
2560      public function test_get_conversation_messages_time_from_and_to() {
2561          // Create some users.
2562          $user1 = self::getDataGenerator()->create_user();
2563          $user2 = self::getDataGenerator()->create_user();
2564          $user3 = self::getDataGenerator()->create_user();
2565          $user4 = self::getDataGenerator()->create_user();
2566  
2567          // Create group conversation.
2568          $conversation = api::create_conversation(
2569              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2570              [$user1->id, $user2->id, $user3->id, $user4->id]
2571          );
2572  
2573          // The person doing the search.
2574          $this->setUser($user1);
2575  
2576          // Send some messages back and forth.
2577          $time = 1;
2578          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1);
2579          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2);
2580          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3);
2581          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4);
2582  
2583          // Retrieve the messages from $time + 2 up until $time + 3, which should be 2nd and 3rd message.
2584          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 0, 0,
2585              'timecreated ASC', $time + 2, $time + 3);
2586  
2587          // Confirm the conversation id is correct.
2588          $this->assertEquals($conversation->id, $convmessages['id']);
2589  
2590          // Confirm the message data is correct.
2591          $messages = $convmessages['messages'];
2592          $this->assertEquals(2, count($messages));
2593  
2594          $message1 = $messages[0];
2595          $message2 = $messages[1];
2596  
2597          $this->assertStringContainsString('Message 2', $message1->text);
2598          $this->assertStringContainsString('Message 3', $message2->text);
2599  
2600          // Confirm the members data is correct.
2601          $members = $convmessages['members'];
2602          $this->assertEquals(2, count($members));
2603      }
2604  
2605  
2606      /**
2607       * Test retrieving conversation messages by providing a limitfrom value.
2608       */
2609      public function test_get_conversation_messages_limitfrom_only() {
2610          // Create some users.
2611          $user1 = self::getDataGenerator()->create_user();
2612          $user2 = self::getDataGenerator()->create_user();
2613          $user3 = self::getDataGenerator()->create_user();
2614          $user4 = self::getDataGenerator()->create_user();
2615  
2616          // Create group conversation.
2617          $conversation = api::create_conversation(
2618              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2619              [$user1->id, $user2->id, $user3->id, $user4->id]
2620          );
2621  
2622          // The person doing the search.
2623          $this->setUser($user1);
2624  
2625          // Send some messages back and forth.
2626          $time = 1;
2627          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1);
2628          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2);
2629          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3);
2630          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4);
2631  
2632          // Retrieve the messages from $time, which should be all of them.
2633          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 2);
2634  
2635          // Confirm the conversation id is correct.
2636          $messages = $convmessages['messages'];
2637          $this->assertEquals($conversation->id, $convmessages['id']);
2638  
2639          // Confirm the message data is correct.
2640          $this->assertEquals(2, count($messages));
2641  
2642          $message1 = $messages[0];
2643          $message2 = $messages[1];
2644  
2645          $this->assertStringContainsString('Message 3', $message1->text);
2646          $this->assertStringContainsString('Message 4', $message2->text);
2647  
2648          // Confirm the members data is correct.
2649          $members = $convmessages['members'];
2650          $this->assertEquals(2, count($members));
2651      }
2652  
2653      /**
2654       * Test retrieving conversation messages by providing a limitnum value.
2655       */
2656      public function test_get_conversation_messages_limitnum() {
2657          // Create some users.
2658          $user1 = self::getDataGenerator()->create_user();
2659          $user2 = self::getDataGenerator()->create_user();
2660          $user3 = self::getDataGenerator()->create_user();
2661          $user4 = self::getDataGenerator()->create_user();
2662  
2663          // Create group conversation.
2664          $conversation = api::create_conversation(
2665              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2666              [$user1->id, $user2->id, $user3->id, $user4->id]
2667          );
2668  
2669          // The person doing the search.
2670          $this->setUser($user1);
2671  
2672          // Send some messages back and forth.
2673          $time = 1;
2674          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1);
2675          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2);
2676          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3);
2677          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4);
2678  
2679          // Retrieve the messages from $time, which should be all of them.
2680          $convmessages = api::get_conversation_messages($user1->id, $conversation->id, 2, 1);
2681  
2682          // Confirm the conversation id is correct.
2683          $messages = $convmessages['messages'];
2684          $this->assertEquals($conversation->id, $convmessages['id']);
2685  
2686          // Confirm the message data is correct.
2687          $messages = $convmessages['messages'];
2688          $this->assertEquals(1, count($messages));
2689  
2690          $message1 = $messages[0];
2691  
2692          $this->assertStringContainsString('Message 3', $message1->text);
2693  
2694          // Confirm the members data is correct.
2695          $members = $convmessages['members'];
2696          $this->assertEquals(1, count($members));
2697      }
2698  
2699      /**
2700       * Tests retrieving most recent conversation message.
2701       */
2702      public function test_get_most_recent_conversation_message() {
2703          // Create some users.
2704          $user1 = self::getDataGenerator()->create_user();
2705          $user2 = self::getDataGenerator()->create_user();
2706          $user3 = self::getDataGenerator()->create_user();
2707  
2708          // Create group conversation.
2709          $conversation = api::create_conversation(
2710              api::MESSAGE_CONVERSATION_TYPE_GROUP,
2711              [$user1->id, $user2->id, $user3->id]
2712          );
2713  
2714          // The person getting the most recent conversation message.
2715          $this->setUser($user1);
2716  
2717          // Send some messages back and forth.
2718          $time = 1;
2719          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time + 1);
2720          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Sup mang?', $time + 2);
2721          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Writing PHPUnit tests!', $time + 3);
2722          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Word.', $time + 4);
2723  
2724          // Retrieve the most recent messages.
2725          $message = api::get_most_recent_conversation_message($conversation->id, $user1->id);
2726  
2727          // Check the results are correct.
2728          $this->assertEquals($user2->id, $message->useridfrom);
2729          $this->assertStringContainsString('Word.', $message->text);
2730      }
2731  
2732      /**
2733       * Tests checking if a user can mark all messages as read.
2734       */
2735      public function test_can_mark_all_messages_as_read() {
2736          // Set as the admin.
2737          $this->setAdminUser();
2738  
2739          // Create some users.
2740          $user1 = self::getDataGenerator()->create_user();
2741          $user2 = self::getDataGenerator()->create_user();
2742          $user3 = self::getDataGenerator()->create_user();
2743  
2744          // Send some messages back and forth.
2745          $time = 1;
2746          $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
2747          $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
2748</