MDL-55725 search: Index proper time modified with indexed files
[moodle.git] / search / engine / solr / tests / engine_test.php
CommitLineData
95c6aeaf
DM
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 * Solr earch engine base unit tests.
19 *
20 * Required params:
21 * - define('TEST_SEARCH_SOLR_HOSTNAME', '127.0.0.1');
22 * - define('TEST_SEARCH_SOLR_PORT', '8983');
23 * - define('TEST_SEARCH_SOLR_INDEXNAME', 'unittest');
24 *
25 * Optional params:
26 * - define('TEST_SEARCH_SOLR_USERNAME', '');
27 * - define('TEST_SEARCH_SOLR_PASSWORD', '');
5dc4624c
EM
28 * - define('TEST_SEARCH_SOLR_SSLCERT', '');
29 * - define('TEST_SEARCH_SOLR_SSLKEY', '');
30 * - define('TEST_SEARCH_SOLR_KEYPASSWORD', '');
31 * - define('TEST_SEARCH_SOLR_CAINFOCERT', '');
95c6aeaf
DM
32 *
33 * @package core_search
34 * @category phpunit
35 * @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
38
39defined('MOODLE_INTERNAL') || die();
40
41global $CFG;
42require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
43require_once($CFG->dirroot . '/search/tests/fixtures/mock_search_area.php');
053118a1 44require_once($CFG->dirroot . '/search/engine/solr/tests/fixtures/testable_engine.php');
95c6aeaf
DM
45
46/**
47 * Solr search engine base unit tests.
48 *
49 * @package core_search
50 * @category phpunit
51 * @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
52 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
53 */
54class search_solr_engine_testcase extends advanced_testcase {
55
56 /**
57 * @var \core_search::manager
58 */
59 protected $search = null;
60
053118a1
EM
61 /**
62 * @var Instace of core_search_generator.
63 */
64 protected $generator = null;
65
66 /**
67 * @var Instace of testable_engine.
68 */
69 protected $engine = null;
70
95c6aeaf
DM
71 public function setUp() {
72 $this->resetAfterTest();
73 set_config('enableglobalsearch', true);
74
75 if (!function_exists('solr_get_version')) {
76 $this->markTestSkipped('Solr extension is not loaded.');
77 }
78
79 if (!defined('TEST_SEARCH_SOLR_HOSTNAME') || !defined('TEST_SEARCH_SOLR_INDEXNAME') ||
80 !defined('TEST_SEARCH_SOLR_PORT')) {
81 $this->markTestSkipped('Solr extension test server not set.');
82 }
83
bd5d2c60
DM
84 set_config('server_hostname', TEST_SEARCH_SOLR_HOSTNAME, 'search_solr');
85 set_config('server_port', TEST_SEARCH_SOLR_PORT, 'search_solr');
95c6aeaf
DM
86 set_config('indexname', TEST_SEARCH_SOLR_INDEXNAME, 'search_solr');
87
88 if (defined('TEST_SEARCH_SOLR_USERNAME')) {
5dc4624c 89 set_config('server_username', TEST_SEARCH_SOLR_USERNAME, 'search_solr');
95c6aeaf
DM
90 }
91
92 if (defined('TEST_SEARCH_SOLR_PASSWORD')) {
5dc4624c 93 set_config('server_password', TEST_SEARCH_SOLR_PASSWORD, 'search_solr');
95c6aeaf
DM
94 }
95
5dc4624c
EM
96 if (defined('TEST_SEARCH_SOLR_SSLCERT')) {
97 set_config('secure', true, 'search_solr');
98 set_config('ssl_cert', TEST_SEARCH_SOLR_SSLCERT, 'search_solr');
99 }
100
101 if (defined('TEST_SEARCH_SOLR_SSLKEY')) {
102 set_config('ssl_key', TEST_SEARCH_SOLR_SSLKEY, 'search_solr');
103 }
104
105 if (defined('TEST_SEARCH_SOLR_KEYPASSWORD')) {
106 set_config('ssl_keypassword', TEST_SEARCH_SOLR_KEYPASSWORD, 'search_solr');
107 }
108
109 if (defined('TEST_SEARCH_SOLR_CAINFOCERT')) {
110 set_config('ssl_cainfo', TEST_SEARCH_SOLR_CAINFOCERT, 'search_solr');
111 }
112
8dfc6291 113 set_config('fileindexing', 1, 'search_solr');
cd894f84
EM
114
115 // We are only test indexing small string files, so setting this as low as we can.
116 set_config('maxindexfilekb', 1, 'search_solr');
5dc4624c 117
053118a1
EM
118 $this->generator = self::getDataGenerator()->get_plugin_generator('core_search');
119 $this->generator->setup();
120
95c6aeaf
DM
121 // Inject search solr engine into the testable core search as we need to add the mock
122 // search component to it.
053118a1
EM
123 $this->engine = new \search_solr\testable_engine();
124 $this->search = testable_core_search::instance($this->engine);
125 $areaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
126 $this->search->add_search_area($areaid, new core_mocksearch\search\mock_search_area());
95c6aeaf
DM
127
128 $this->setAdminUser();
129
130 // Cleanup before doing anything on it as the index it is out of this test control.
131 $this->search->delete_index();
132
133 // Add moodle fields if they don't exist.
134 $schema = new \search_solr\schema();
135 $schema->setup(false);
136 }
137
053118a1
EM
138 public function tearDown() {
139 // For unit tests before PHP 7, teardown is called even on skip. So only do our teardown if we did setup.
140 if ($this->generator) {
141 // Moodle DML freaks out if we don't teardown the temp table after each run.
142 $this->generator->teardown();
143 $this->generator = null;
144 }
145 }
146
147 /**
148 * Simple data provider to allow tests to be run with file indexing on and off.
149 */
150 public function file_indexing_provider() {
151 return array(
152 'file-indexing-on' => array(1),
153 'file-indexing-off' => array(0)
154 );
155 }
156
95c6aeaf 157 public function test_connection() {
053118a1 158 $this->assertTrue($this->engine->is_server_ready());
95c6aeaf
DM
159 }
160
053118a1
EM
161 /**
162 * @dataProvider file_indexing_provider
163 */
164 public function test_index($fileindexing) {
95c6aeaf
DM
165 global $DB;
166
053118a1
EM
167 $this->engine->test_set_config('fileindexing', $fileindexing);
168
169 $record = new \stdClass();
170 $record->timemodified = time() - 1;
171 $this->generator->create_record($record);
95c6aeaf
DM
172
173 // Data gets into the search engine.
174 $this->assertTrue($this->search->index());
175
176 // Not anymore as everything was already added.
177 sleep(1);
178 $this->assertFalse($this->search->index());
179
053118a1 180 $this->generator->create_record();
95c6aeaf
DM
181
182 // Indexing again once there is new data.
183 $this->assertTrue($this->search->index());
184 }
185
186 /**
187 * Better keep this not very strict about which or how many results are returned as may depend on solr engine config.
188 *
053118a1
EM
189 * @dataProvider file_indexing_provider
190 *
95c6aeaf
DM
191 * @return void
192 */
053118a1 193 public function test_search($fileindexing) {
95c6aeaf
DM
194 global $USER, $DB;
195
053118a1
EM
196 $this->engine->test_set_config('fileindexing', $fileindexing);
197
198 $this->generator->create_record();
199 $record = new \stdClass();
200 $record->title = "Special title";
201 $this->generator->create_record($record);
95c6aeaf
DM
202
203 $this->search->index();
204
205 $querydata = new stdClass();
206 $querydata->q = 'message';
207 $results = $this->search->search($querydata);
208 $this->assertCount(2, $results);
209
210 // Based on core_mocksearch\search\indexer.
211 $this->assertEquals($USER->id, $results[0]->get('userid'));
212 $this->assertEquals(\context_system::instance()->id, $results[0]->get('contextid'));
213
546c0af5 214 // Do a test to make sure we aren't searching non-query fields, like areaid.
053118a1 215 $querydata->q = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
546c0af5
EM
216 $this->assertCount(0, $this->search->search($querydata));
217 $querydata->q = 'message';
218
95c6aeaf
DM
219 sleep(1);
220 $beforeadding = time();
221 sleep(1);
053118a1 222 $this->generator->create_record();
95c6aeaf
DM
223 $this->search->index();
224
225 // Timestart.
226 $querydata->timestart = $beforeadding;
227 $this->assertCount(1, $this->search->search($querydata));
228
229 // Timeend.
230 unset($querydata->timestart);
231 $querydata->timeend = $beforeadding;
232 $this->assertCount(2, $this->search->search($querydata));
233
234 // Title.
235 unset($querydata->timeend);
053118a1 236 $querydata->title = 'Special title';
95c6aeaf 237 $this->assertCount(1, $this->search->search($querydata));
3dab4841 238
427e3cbc
EM
239 // Course IDs.
240 unset($querydata->title);
241 $querydata->courseids = array(SITEID + 1);
242 $this->assertCount(0, $this->search->search($querydata));
243
244 $querydata->courseids = array(SITEID);
245 $this->assertCount(3, $this->search->search($querydata));
246
501801a2
EM
247 // Now try some area-id combinations.
248 unset($querydata->courseids);
249 $forumpostareaid = \core_search\manager::generate_areaid('mod_forum', 'post');
861de101 250 $mockareaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
501801a2
EM
251
252 $querydata->areaids = array($forumpostareaid);
253 $this->assertCount(0, $this->search->search($querydata));
254
255 $querydata->areaids = array($forumpostareaid, $mockareaid);
256 $this->assertCount(3, $this->search->search($querydata));
257
258 $querydata->areaids = array($mockareaid);
259 $this->assertCount(3, $this->search->search($querydata));
260
261 $querydata->areaids = array();
262 $this->assertCount(3, $this->search->search($querydata));
263
3dab4841 264 // Check that index contents get updated.
053118a1 265 $this->generator->delete_all();
3dab4841
DM
266 $this->search->index(true);
267 unset($querydata->title);
053118a1 268 $querydata->q = '*';
3dab4841 269 $this->assertCount(0, $this->search->search($querydata));
95c6aeaf
DM
270 }
271
053118a1
EM
272 /**
273 * @dataProvider file_indexing_provider
274 */
275 public function test_delete($fileindexing) {
276 $this->engine->test_set_config('fileindexing', $fileindexing);
277
278 $this->generator->create_record();
279 $this->generator->create_record();
95c6aeaf
DM
280 $this->search->index();
281
282 $querydata = new stdClass();
283 $querydata->q = 'message';
284
285 $this->assertCount(2, $this->search->search($querydata));
286
053118a1 287 $areaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
95c6aeaf 288 $this->search->delete_index($areaid);
95c6aeaf
DM
289 $this->assertCount(0, $this->search->search($querydata));
290 }
f6b425e2 291
053118a1
EM
292 /**
293 * @dataProvider file_indexing_provider
294 */
295 public function test_alloweduserid($fileindexing) {
296 $this->engine->test_set_config('fileindexing', $fileindexing);
f6b425e2 297
053118a1
EM
298 $area = new core_mocksearch\search\mock_search_area();
299
300 $record = $this->generator->create_record();
f6b425e2
EM
301
302 // Get the doc and insert the default doc.
303 $doc = $area->get_document($record);
053118a1 304 $this->engine->add_document($doc);
f6b425e2
EM
305
306 $users = array();
307 $users[] = $this->getDataGenerator()->create_user();
308 $users[] = $this->getDataGenerator()->create_user();
309 $users[] = $this->getDataGenerator()->create_user();
310
311 // Add a record that only user 100 can see.
312 $originalid = $doc->get('id');
313
314 // Now add a custom doc for each user.
315 foreach ($users as $user) {
091973db 316 $doc = $area->get_document($record);
f6b425e2
EM
317 $doc->set('id', $originalid.'-'.$user->id);
318 $doc->set('owneruserid', $user->id);
053118a1 319 $this->engine->add_document($doc);
f6b425e2
EM
320 }
321
053118a1 322 $this->engine->area_index_complete($area->get_area_id());
f6b425e2
EM
323
324 $querydata = new stdClass();
325 $querydata->q = 'message';
326 $querydata->title = $doc->get('title');
327
328 // We are going to go through each user and see if they get the original and the owned doc.
329 foreach ($users as $user) {
330 $this->setUser($user);
331
332 $results = $this->search->search($querydata);
333 $this->assertCount(2, $results);
334
335 $owned = 0;
336 $notowned = 0;
337
338 // We don't know what order we will get the results in, so we are doing this.
339 foreach ($results as $result) {
340 $owneruserid = $result->get('owneruserid');
341 if (empty($owneruserid)) {
342 $notowned++;
343 $this->assertEquals(0, $owneruserid);
344 $this->assertEquals($originalid, $result->get('id'));
345 } else {
346 $owned++;
347 $this->assertEquals($user->id, $owneruserid);
348 $this->assertEquals($originalid.'-'.$user->id, $result->get('id'));
349 }
350 }
351
352 $this->assertEquals(1, $owned);
353 $this->assertEquals(1, $notowned);
354 }
355
356 // Now test a user with no owned results.
357 $otheruser = $this->getDataGenerator()->create_user();
358 $this->setUser($otheruser);
359
360 $results = $this->search->search($querydata);
361 $this->assertCount(1, $results);
362
363 $this->assertEquals(0, $results[0]->get('owneruserid'));
364 $this->assertEquals($originalid, $results[0]->get('id'));
365 }
4894840d 366
053118a1
EM
367 /**
368 * @dataProvider file_indexing_provider
369 */
370 public function test_highlight($fileindexing) {
4894840d
EM
371 global $PAGE;
372
053118a1
EM
373 $this->engine->test_set_config('fileindexing', $fileindexing);
374
375 $this->generator->create_record();
4894840d
EM
376 $this->search->index();
377
378 $querydata = new stdClass();
379 $querydata->q = 'message';
380
381 $results = $this->search->search($querydata);
053118a1 382 $this->assertCount(1, $results);
4894840d
EM
383
384 $result = reset($results);
385
386 $regex = '|'.\search_solr\engine::HIGHLIGHT_START.'message'.\search_solr\engine::HIGHLIGHT_END.'|';
387 $this->assertRegExp($regex, $result->get('content'));
388
389 $searchrenderer = $PAGE->get_renderer('core_search');
390 $exported = $result->export_for_template($searchrenderer);
391
392 $regex = '|<span class="highlight">message</span>|';
393 $this->assertRegExp($regex, $exported['content']);
394 }
e195da19 395
1aaead91
EM
396 public function test_export_file_for_engine() {
397 // Get area to work with.
398 $areaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
399 $area = \core_search\manager::get_search_area($areaid);
400
401 $record = $this->generator->create_record();
402
403 $doc = $area->get_document($record);
404 $filerecord = new stdClass();
405 $filerecord->timemodified = 978310800;
406 $file = $this->generator->create_file($filerecord);
407 $doc->add_stored_file($file);
408
409 $filearray = $doc->export_file_for_engine($file);
410
411 $this->assertEquals(\core_search\manager::TYPE_FILE, $filearray['type']);
412 $this->assertEquals($file->get_id(), $filearray['solr_fileid']);
413 $this->assertEquals($file->get_contenthash(), $filearray['solr_filecontenthash']);
414 $this->assertEquals(\search_solr\document::INDEXED_FILE_TRUE, $filearray['solr_fileindexstatus']);
415 $this->assertEquals($file->get_filename(), $filearray['title']);
416 $this->assertEquals(978310800, \search_solr\document::import_time_from_engine($filearray['modified']));
417 }
418
cd894f84 419 public function test_index_file() {
cd894f84 420 // Very simple test.
053118a1
EM
421 $file = $this->generator->create_file();
422
423 $record = new \stdClass();
424 $record->attachfileids = array($file->get_id());
425 $this->generator->create_record($record);
426
cd894f84
EM
427 $this->search->index();
428 $querydata = new stdClass();
429 $querydata->q = '"File contents"';
430
053118a1 431 $this->assertCount(1, $this->search->search($querydata));
cd894f84
EM
432 }
433
434 public function test_reindexing_files() {
053118a1
EM
435 // Get area to work with.
436 $areaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
cd894f84
EM
437 $area = \core_search\manager::get_search_area($areaid);
438
053118a1 439 $record = $this->generator->create_record();
cd894f84
EM
440
441 $doc = $area->get_document($record);
442
443 // Now we are going to make some files.
444 $fs = get_file_storage();
445 $syscontext = \context_system::instance();
446
447 $files = array();
cd894f84 448
053118a1 449 $filerecord = new \stdClass();
cd894f84
EM
450 // We make enough so that we pass the 500 files threashold. That is the boundary when getting files.
451 $boundary = 500;
452 $top = (int)($boundary * 1.1);
453 for ($i = 0; $i < $top; $i++) {
053118a1
EM
454 $filerecord->filename = 'searchfile'.$i;
455 $filerecord->content = 'Some FileContents'.$i;
456 $file = $this->generator->create_file($filerecord);
cd894f84
EM
457 $doc->add_stored_file($file);
458 $files[] = $file;
459 }
460
461 // Add the doc with lots of files, then commit.
053118a1
EM
462 $this->engine->add_document($doc, true);
463 $this->engine->area_index_complete($area->get_area_id());
cd894f84
EM
464
465 // Indexes we are going to check. 0 means we will delete, 1 means we will keep.
466 $checkfiles = array(
467 0 => 0, // Check the begining of the set.
468 1 => 1,
469 2 => 0,
470 ($top - 3) => 0, // Check the end of the set.
471 ($top - 2) => 1,
472 ($top - 1) => 0,
473 ($boundary - 2) => 0, // Check at the boundary between fetch groups.
474 ($boundary - 1) => 0,
475 $boundary => 0,
476 ($boundary + 1) => 0,
477 ((int)($boundary * 0.5)) => 1, // Make sure we keep some middle ones.
478 ((int)($boundary * 1.05)) => 1
479 );
480
481 $querydata = new stdClass();
482
483 // First, check that all the files are currently there.
484 foreach ($checkfiles as $key => $unused) {
485 $querydata->q = 'FileContents'.$key;
486 $this->assertCount(1, $this->search->search($querydata));
487 $querydata->q = 'searchfile'.$key;
488 $this->assertCount(1, $this->search->search($querydata));
489 }
490
491 // Remove the files we want removed from the files array.
492 foreach ($checkfiles as $key => $keep) {
493 if (!$keep) {
494 unset($files[$key]);
495 }
496 }
497
498 // And make us a new file to add.
053118a1
EM
499 $filerecord->filename = 'searchfileNew';
500 $filerecord->content = 'Some FileContentsNew';
501 $files[] = $this->generator->create_file($filerecord);
cd894f84
EM
502 $checkfiles['New'] = 1;
503
504 $doc = $area->get_document($record);
505 foreach($files as $file) {
506 $doc->add_stored_file($file);
507 }
508
509 // Reindex the document with the changed files.
053118a1
EM
510 $this->engine->add_document($doc, true);
511 $this->engine->area_index_complete($area->get_area_id());
cd894f84
EM
512
513 // Go through our check array, and see if the file is there or not.
514 foreach ($checkfiles as $key => $keep) {
515 $querydata->q = 'FileContents'.$key;
516 $this->assertCount($keep, $this->search->search($querydata));
517 $querydata->q = 'searchfile'.$key;
518 $this->assertCount($keep, $this->search->search($querydata));
519 }
520
521 // Now check that we get one result when we search from something in all of them.
522 $querydata->q = 'Some';
523 $this->assertCount(1, $this->search->search($querydata));
524 }
525
053118a1
EM
526 /**
527 * Test indexing a file we don't consider indexable.
528 */
cd894f84 529 public function test_index_filtered_file() {
053118a1
EM
530 // Get area to work with.
531 $areaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
cd894f84
EM
532 $area = \core_search\manager::get_search_area($areaid);
533
534 // Get a single record to make a doc from.
053118a1 535 $record = $this->generator->create_record();
cd894f84
EM
536
537 $doc = $area->get_document($record);
538
539 // Now we are going to make some files.
540 $fs = get_file_storage();
541 $syscontext = \context_system::instance();
542
cd894f84 543 // We need to make a file greater than 1kB in size, which is the lowest filter size.
053118a1
EM
544 $filerecord = new \stdClass();
545 $filerecord->filename = 'largefile';
546 $filerecord->content = 'Some LargeFindContent to find.';
cd894f84 547 for ($i = 0; $i < 200; $i++) {
053118a1 548 $filerecord->content .= ' The quick brown fox jumps over the lazy dog.';
cd894f84
EM
549 }
550
053118a1 551 $this->assertGreaterThan(1024, strlen($filerecord->content));
cd894f84 552
053118a1 553 $file = $this->generator->create_file($filerecord);
cd894f84
EM
554 $doc->add_stored_file($file);
555
053118a1
EM
556 $filerecord->filename = 'smallfile';
557 $filerecord->content = 'Some SmallFindContent to find.';
558 $file = $this->generator->create_file($filerecord);
cd894f84
EM
559 $doc->add_stored_file($file);
560
053118a1
EM
561 $this->engine->add_document($doc, true);
562 $this->engine->area_index_complete($area->get_area_id());
cd894f84
EM
563
564 $querydata = new stdClass();
565 // We shouldn't be able to find the large file contents.
566 $querydata->q = 'LargeFindContent';
567 $this->assertCount(0, $this->search->search($querydata));
568
569 // But we should be able to find the filename.
570 $querydata->q = 'largefile';
571 $this->assertCount(1, $this->search->search($querydata));
572
573 // We should be able to find the small file contents.
574 $querydata->q = 'SmallFindContent';
575 $this->assertCount(1, $this->search->search($querydata));
576
577 // And we should be able to find the filename.
578 $querydata->q = 'smallfile';
579 $this->assertCount(1, $this->search->search($querydata));
580 }
581
582 public function test_delete_by_id() {
cd894f84 583 // First get files in the index.
053118a1
EM
584 $file = $this->generator->create_file();
585 $record = new \stdClass();
586 $record->attachfileids = array($file->get_id());
587 $this->generator->create_record($record);
588 $this->generator->create_record($record);
cd894f84 589 $this->search->index();
cd894f84
EM
590
591 $querydata = new stdClass();
592
593 // Then search to make sure they are there.
594 $querydata->q = '"File contents"';
595 $results = $this->search->search($querydata);
596 $this->assertCount(2, $results);
597
598 $first = reset($results);
599 $deleteid = $first->get('id');
600
053118a1 601 $this->engine->delete_by_id($deleteid);
cd894f84
EM
602
603 // Check that we don't get a result for it anymore.
604 $results = $this->search->search($querydata);
605 $this->assertCount(1, $results);
606 $result = reset($results);
607 $this->assertNotEquals($deleteid, $result->get('id'));
608 }
053118a1
EM
609
610 /**
611 * Test that expected results are returned, even with low check_access success rate.
612 *
613 * @dataProvider file_indexing_provider
614 */
615 public function test_solr_filling($fileindexing) {
616 $this->engine->test_set_config('fileindexing', $fileindexing);
617
618 $user1 = self::getDataGenerator()->create_user();
619 $user2 = self::getDataGenerator()->create_user();
620
621 // We are going to create a bunch of records that user 1 can see with 2 keywords.
622 // Then we are going to create a bunch for user 2 with only 1 of the keywords.
623 // If user 2 searches for both keywords, solr will return all of the user 1 results, then the user 2 results.
624 // This is because the user 1 results will match 2 keywords, while the others will match only 1.
625
626 $record = new \stdClass();
627
628 // First create a bunch of records for user 1 to see.
629 $record->denyuserids = array($user2->id);
630 $record->content = 'Something1 Something2';
631 $maxresults = (int)(\core_search\manager::MAX_RESULTS * .75);
632 for ($i = 0; $i < $maxresults; $i++) {
633 $this->generator->create_record($record);
634 }
635
636 // Then create a bunch of records for user 2 to see.
637 $record->denyuserids = array($user1->id);
638 $record->content = 'Something1';
639 for ($i = 0; $i < $maxresults; $i++) {
640 $this->generator->create_record($record);
641 }
642
643 $this->search->index();
644
645 // Check that user 1 sees all their results.
646 $this->setUser($user1);
647 $querydata = new stdClass();
648 $querydata->q = 'Something1 Something2';
649 $results = $this->search->search($querydata);
650 $this->assertCount($maxresults, $results);
651
652 // Check that user 2 will see theirs, even though they may be crouded out.
653 $this->setUser($user2);
654 $results = $this->search->search($querydata);
655 $this->assertCount($maxresults, $results);
656 }
657
658 /**
659 * Create 40 docs, that will be return from Solr in 10 hidden, 10 visible, 10 hidden, 10 visible if you query for:
660 * Something1 Something2 Something3 Something4, with the specified user set.
661 */
662 protected function setup_user_hidden_docs($user) {
663 // These results will come first, and will not be visible by the user.
664 $record = new \stdClass();
665 $record->denyuserids = array($user->id);
666 $record->content = 'Something1 Something2 Something3 Something4';
667 for ($i = 0; $i < 10; $i++) {
668 $this->generator->create_record($record);
669 }
670
671 // These results will come second, and will be visible by the user.
672 unset($record->denyuserids);
673 $record->content = 'Something1 Something2 Something3';
674 for ($i = 0; $i < 10; $i++) {
675 $this->generator->create_record($record);
676 }
677
678 // These results will come third, and will not be visible by the user.
679 $record->denyuserids = array($user->id);
680 $record->content = 'Something1 Something2';
681 for ($i = 0; $i < 10; $i++) {
682 $this->generator->create_record($record);
683 }
684
685 // These results will come fourth, and will be visible by the user.
686 unset($record->denyuserids);
687 $record->content = 'Something1 ';
688 for ($i = 0; $i < 10; $i++) {
689 $this->generator->create_record($record);
690 }
691 }
692
693 /**
694 * Test that counts are what we expect.
695 *
696 * @dataProvider file_indexing_provider
697 */
698 public function test_get_query_total_count($fileindexing) {
699 $this->engine->test_set_config('fileindexing', $fileindexing);
700
701 $user = self::getDataGenerator()->create_user();
702 $this->setup_user_hidden_docs($user);
703 $this->search->index();
704
705 $this->setUser($user);
706 $querydata = new stdClass();
707 $querydata->q = 'Something1 Something2 Something3 Something4';
708
709 // In this first set, it should have determined the first 10 of 40 are bad, so there could be up to 30 left.
710 $results = $this->engine->execute_query($querydata, true, 5);
711 $this->assertEquals(30, $this->engine->get_query_total_count());
712 $this->assertCount(5, $results);
713
714 // To get to 15, it has to process the first 10 that are bad, 10 that are good, 10 that are bad, then 5 that are good.
715 // So we now know 20 are bad out of 40.
716 $results = $this->engine->execute_query($querydata, true, 15);
717 $this->assertEquals(20, $this->engine->get_query_total_count());
718 $this->assertCount(15, $results);
719
720 // Try to get more then all, make sure we still see 20 count and 20 returned.
721 $results = $this->engine->execute_query($querydata, true, 30);
722 $this->assertEquals(20, $this->engine->get_query_total_count());
723 $this->assertCount(20, $results);
724 }
725
726 /**
727 * Test that paged results are what we expect.
728 *
729 * @dataProvider file_indexing_provider
730 */
731 public function test_manager_paged_search($fileindexing) {
732 $this->engine->test_set_config('fileindexing', $fileindexing);
733
734 $user = self::getDataGenerator()->create_user();
735 $this->setup_user_hidden_docs($user);
736 $this->search->index();
737
738 // Check that user 1 sees all their results.
739 $this->setUser($user);
740 $querydata = new stdClass();
741 $querydata->q = 'Something1 Something2 Something3 Something4';
742
743 // On this first page, it should have determined the first 10 of 40 are bad, so there could be up to 30 left.
744 $results = $this->search->paged_search($querydata, 0);
745 $this->assertEquals(30, $results->totalcount);
746 $this->assertCount(10, $results->results);
747 $this->assertEquals(0, $results->actualpage);
748
749 // On the second page, it should have found the next 10 bad ones, so we no know there are only 20 total.
750 $results = $this->search->paged_search($querydata, 1);
751 $this->assertEquals(20, $results->totalcount);
752 $this->assertCount(10, $results->results);
753 $this->assertEquals(1, $results->actualpage);
754
755 // Try to get an additional page - we should get back page 1 results, since that is the last page with valid results.
756 $results = $this->search->paged_search($querydata, 2);
757 $this->assertEquals(20, $results->totalcount);
758 $this->assertCount(10, $results->results);
759 $this->assertEquals(1, $results->actualpage);
760 }
95c6aeaf 761}