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 * Sent message global search unit tests.
19 *
20 * @package     core
21 * @copyright   2016 Devang Gaur
22 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25defined('MOODLE_INTERNAL') || die();
26
27global $CFG;
28require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
29
30/**
31 * Provides the unit tests for sent message global search.
32 *
33 * @package     core
34 * @copyright   2016 Devang Gaur
35 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 */
37class message_sent_search_testcase extends advanced_testcase {
38
39    /**
40     * @var string Area id
41     */
42    protected $messagesentareaid = null;
43
44    /**
45     * Setting up the test environment
46     * @return void
47     */
48    public function setUp(): void {
49        $this->resetAfterTest(true);
50        set_config('enableglobalsearch', true);
51
52        $this->messagesentareaid = \core_search\manager::generate_areaid('core_message', 'message_sent');
53
54        // Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this.
55        $search = testable_core_search::instance();
56    }
57
58    /**
59     * Indexing messages contents.
60     *
61     * @return void
62     */
63    public function test_message_sent_indexing() {
64
65        // Returns the instance as long as the area is supported.
66        $searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
67        $this->assertInstanceOf('\core_message\search\message_sent', $searcharea);
68
69        $user1 = self::getDataGenerator()->create_user();
70        $user2 = self::getDataGenerator()->create_user();
71
72        $this->preventResetByRollback();
73        $sink = $this->redirectMessages();
74
75        $message = new \core\message\message();
76        $message->courseid = SITEID;
77        $message->userfrom = $user1;
78        $message->userto = $user2;
79        $message->subject = "Test Subject";
80        $message->smallmessage = "Test small messsage";
81        $message->fullmessage = "Test full messsage";
82        $message->fullmessageformat = 0;
83        $message->fullmessagehtml = null;
84        $message->notification = 0;
85        $message->component = 'moodle';
86        $message->name = "instantmessage";
87
88        message_send($message);
89
90        $messages = $sink->get_messages();
91
92        $this->assertEquals(1, count($messages));
93
94        // All records.
95        $recordset = $searcharea->get_recordset_by_timestamp(0);
96        $this->assertTrue($recordset->valid());
97        $nrecords = 0;
98        foreach ($recordset as $record) {
99            $this->assertInstanceOf('stdClass', $record);
100            $doc = $searcharea->get_document($record);
101            $this->assertInstanceOf('\core_search\document', $doc);
102            $nrecords++;
103        }
104        // If there would be an error/failure in the foreach above the recordset would be closed on shutdown.
105        $recordset->close();
106        $this->assertEquals(1, $nrecords);
107
108        // The +2 is to prevent race conditions.
109        $recordset = $searcharea->get_recordset_by_timestamp(time() + 2);
110
111        // No new records.
112        $this->assertFalse($recordset->valid());
113        $recordset->close();
114    }
115
116    /**
117     * Indexing messages, with restricted contexts.
118     */
119    public function test_message_sent_indexing_contexts() {
120        global $SITE;
121
122        $searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
123
124        $user1 = self::getDataGenerator()->create_user();
125        $user2 = self::getDataGenerator()->create_user();
126
127        $this->preventResetByRollback();
128        $sink = $this->redirectMessages();
129
130        // Send first message.
131        $message = new \core\message\message();
132        $message->courseid = SITEID;
133        $message->userfrom = $user1;
134        $message->userto = $user2;
135        $message->subject = 'Test1';
136        $message->smallmessage = 'Test small messsage';
137        $message->fullmessage = 'Test full messsage';
138        $message->fullmessageformat = 0;
139        $message->fullmessagehtml = null;
140        $message->notification = 0;
141        $message->component = 'moodle';
142        $message->name = 'instantmessage';
143        message_send($message);
144
145        // Ensure that ordering by timestamp will return in consistent order.
146        $this->waitForSecond();
147
148        // Send second message in opposite direction.
149        $message = new \core\message\message();
150        $message->courseid = SITEID;
151        $message->userfrom = $user2;
152        $message->userto = $user1;
153        $message->subject = 'Test2';
154        $message->smallmessage = 'Test small messsage';
155        $message->fullmessage = 'Test full messsage';
156        $message->fullmessageformat = 0;
157        $message->fullmessagehtml = null;
158        $message->notification = 0;
159        $message->component = 'moodle';
160        $message->name = 'instantmessage';
161        message_send($message);
162
163        // Test function with null context and system context (same).
164        $rs = $searcharea->get_document_recordset(0, null);
165        $this->assertEquals(['Test1', 'Test2'], self::recordset_to_subjects($rs));
166        $rs = $searcharea->get_document_recordset(0, context_system::instance());
167        $this->assertEquals(['Test1', 'Test2'], self::recordset_to_subjects($rs));
168
169        // Test with user context for each user.
170        $rs = $searcharea->get_document_recordset(0, \context_user::instance($user1->id));
171        $this->assertEquals(['Test1'], self::recordset_to_subjects($rs));
172        $rs = $searcharea->get_document_recordset(0, \context_user::instance($user2->id));
173        $this->assertEquals(['Test2'], self::recordset_to_subjects($rs));
174
175        // Test with a course context (should return null).
176        $this->assertNull($searcharea->get_document_recordset(0,
177                context_course::instance($SITE->id)));
178    }
179
180    /**
181     * Utility function to convert recordset to array of message subjects for testing.
182     *
183     * @param moodle_recordset $rs Recordset to convert (and close)
184     * @return array Array of IDs from records indexed by number (0, 1, 2, ...)
185     */
186    public static function recordset_to_subjects(moodle_recordset $rs) {
187        $results = [];
188        foreach ($rs as $rec) {
189            $results[] = $rec->subject;
190        }
191        $rs->close();
192        return $results;
193    }
194
195    /**
196     * Document contents.
197     *
198     * @return void
199     */
200    public function test_message_sent_document() {
201
202        // Returns the instance as long as the area is supported.
203        $searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
204        $this->assertInstanceOf('\core_message\search\message_sent', $searcharea);
205
206        $user1 = self::getDataGenerator()->create_user();
207        $user2 = self::getDataGenerator()->create_user();
208
209        $this->preventResetByRollback();
210        $sink = $this->redirectMessages();
211
212        $message = new \core\message\message();
213        $message->courseid = SITEID;
214        $message->userfrom = $user1;
215        $message->userto = $user2;
216        $message->subject = "Test Subject";
217        $message->smallmessage = "Test small messsage";
218        $message->fullmessage = "Test full messsage";
219        $message->fullmessageformat = 0;
220        $message->fullmessagehtml = null;
221        $message->notification = 0;
222        $message->component = "moodle";
223        $message->name = "instantmessage";
224
225        message_send($message);
226
227        $messages = $sink->get_messages();
228        $message = $messages[0];
229
230        $doc = $searcharea->get_document($message);
231        $this->assertInstanceOf('\core_search\document', $doc);
232        $this->assertEquals($message->id, $doc->get('itemid'));
233        $this->assertEquals($this->messagesentareaid . '-' . $message->id, $doc->get('id'));
234        $this->assertEquals(SITEID, $doc->get('courseid'));
235        $this->assertEquals($message->useridfrom, $doc->get('owneruserid'));
236        $this->assertEquals($message->useridto, $doc->get('userid'));
237        $this->assertEquals(content_to_text($message->subject, false), $doc->get('title'));
238        $this->assertEquals(content_to_text($message->smallmessage, false), $doc->get('content'));
239    }
240
241    /**
242     * Document accesses.
243     *
244     * @return void
245     */
246    public function test_message_sent_access() {
247        global $CFG;
248
249        // Returns the instance as long as the area is supported.
250        $searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
251
252        $user1 = self::getDataGenerator()->create_user();
253        $user2 = self::getDataGenerator()->create_user();
254        $user3 = self::getDataGenerator()->create_user();
255
256        $this->preventResetByRollback();
257        $sink = $this->redirectMessages();
258
259        $message = new \core\message\message();
260        $message->courseid = SITEID;
261        $message->userfrom = $user1;
262        $message->userto = $user2;
263        $message->subject = "Test Subject";
264        $message->smallmessage = "Test small messsage";
265        $message->fullmessage = "Test full messsage";
266        $message->fullmessageformat = 0;
267        $message->fullmessagehtml = null;
268        $message->notification = 0;
269        $message->component = "moodle";
270        $message->name = "instantmessage";
271
272        $messageid = message_send($message);
273
274        $messages = $sink->get_messages();
275        $message = $messages[0];
276
277        $this->setUser($user1);
278
279        if ($CFG->messaging) {
280            $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($messageid));
281        } else {
282            $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
283        }
284
285        $this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access(-123));
286
287        \core_message\api::delete_message($user1->id, $message->id);
288        $this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access($messageid));
289
290        $this->setUser($user2);
291        $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
292
293        $this->setUser($user3);
294        $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
295
296        $this->setGuestUser();
297        $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
298
299        $this->setAdminUser();
300        $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
301
302        delete_user($user2);
303
304        $this->setUser($user1);
305        $this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access($messageid));
306
307    }
308
309    /**
310     * Test sent deleted user.
311     * Tests the case where a sent message for a deleted user
312     * is attempted to be added to the index.
313     *
314     * @return void
315     */
316    public function test_message_sent_deleted_user() {
317
318        // Returns the instance as long as the area is supported.
319        $searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
320        $this->assertInstanceOf('\core_message\search\message_sent', $searcharea);
321
322        $user1 = self::getDataGenerator()->create_user();
323        $user2 = self::getDataGenerator()->create_user();
324
325        $this->preventResetByRollback();
326        $sink = $this->redirectMessages();
327
328        $message = new \core\message\message();
329        $message->courseid = SITEID;
330        $message->userfrom = $user1;
331        $message->userto = $user2;
332        $message->subject = "Test Subject";
333        $message->smallmessage = "Test small messsage";
334        $message->fullmessage = "Test full messsage";
335        $message->fullmessageformat = 0;
336        $message->fullmessagehtml = null;
337        $message->notification = 0;
338        $message->component = "moodle";
339        $message->name = "instantmessage";
340
341        message_send($message);
342
343        $messages = $sink->get_messages();
344        $message = $messages[0];
345
346        // Delete user.
347        delete_user($user1);
348
349        $doc = $searcharea->get_document($message);
350
351        $this->assertFalse($doc);
352
353    }
354
355    /**
356     * Test document icon.
357     */
358    public function test_get_doc_icon() {
359        $searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
360
361        $document = $this->getMockBuilder('\core_search\document')
362            ->disableOriginalConstructor()
363            ->getMock();
364
365        $result = $searcharea->get_doc_icon($document);
366
367        $this->assertEquals('t/message', $result->get_name());
368        $this->assertEquals('moodle', $result->get_component());
369    }
370
371    /**
372     * Test assigned search categories.
373     */
374    public function test_get_category_names() {
375        $searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
376
377        $expected = ['core-users'];
378        $this->assertEquals($expected, $searcharea->get_category_names());
379    }
380}
381