Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 9 May 2022 (12 months).
  • Bug fixes for security issues in 3.11.x will end 14 November 2022 (18 months).
  • 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 35 and 311] [Versions 36 and 311] [Versions 37 and 311] [Versions 38 and 311] [Versions 39 and 311]

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