9ed52444ea9110da8bce9212bd954bd596e7678e
[lhc/web/wiklou.git] / tests / phpunit / includes / search / SearchEnginePrefixTest.php
1 <?php
2 use MediaWiki\MediaWikiServices;
3
4 /**
5 * @group Search
6 * @group Database
7 */
8 class SearchEnginePrefixTest extends MediaWikiLangTestCase {
9
10 /**
11 * @var SearchEngine
12 */
13 private $search;
14
15 public function addDBDataOnce() {
16 if ( !$this->isWikitextNS( NS_MAIN ) ) {
17 // tests are skipped if NS_MAIN is not wikitext
18 return;
19 }
20
21 $this->insertPage( 'Sandbox' );
22 $this->insertPage( 'Bar' );
23 $this->insertPage( 'Example' );
24 $this->insertPage( 'Example Bar' );
25 $this->insertPage( 'Example Foo' );
26 $this->insertPage( 'Example Foo/Bar' );
27 $this->insertPage( 'Example/Baz' );
28 $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
29 $this->insertPage( 'Redirect Test' );
30 $this->insertPage( 'Redirect Test Worse Result' );
31 $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
32 $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
33 $this->insertPage( 'Redirect Test2' );
34 $this->insertPage( 'Redirect Test2 Worse Result' );
35
36 $this->insertPage( 'Talk:Sandbox' );
37 $this->insertPage( 'Talk:Example' );
38
39 $this->insertPage( 'User:Example' );
40 }
41
42 protected function setUp() {
43 parent::setUp();
44
45 if ( !$this->isWikitextNS( NS_MAIN ) ) {
46 $this->markTestSkipped( 'Main namespace does not support wikitext.' );
47 }
48
49 // Avoid special pages from extensions interferring with the tests
50 $this->setMwGlobals( 'wgSpecialPages', [] );
51 $this->search = MediaWikiServices::getInstance()->newSearchEngine();
52 $this->search->setNamespaces( [] );
53 }
54
55 protected function searchProvision( array $results = null ) {
56 if ( $results === null ) {
57 $this->setMwGlobals( 'wgHooks', [] );
58 } else {
59 $this->setMwGlobals( 'wgHooks', [
60 'PrefixSearchBackend' => [
61 function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
62 $srchres = $results;
63 return false;
64 }
65 ],
66 ] );
67 }
68 }
69
70 public static function provideSearch() {
71 return [
72 [ [
73 'Empty string',
74 'query' => '',
75 'results' => [],
76 ] ],
77 [ [
78 'Main namespace with title prefix',
79 'query' => 'Ex',
80 'results' => [
81 'Example',
82 'Example/Baz',
83 'Example Bar',
84 ],
85 // Third result when testing offset
86 'offsetresult' => [
87 'Example Foo',
88 ],
89 ] ],
90 [ [
91 'Talk namespace prefix',
92 'query' => 'Talk:',
93 'results' => [
94 'Talk:Example',
95 'Talk:Sandbox',
96 ],
97 ] ],
98 [ [
99 'User namespace prefix',
100 'query' => 'User:',
101 'results' => [
102 'User:Example',
103 ],
104 ] ],
105 [ [
106 'Special namespace prefix',
107 'query' => 'Special:',
108 'results' => [
109 'Special:ActiveUsers',
110 'Special:AllMessages',
111 'Special:AllMyFiles',
112 ],
113 // Third result when testing offset
114 'offsetresult' => [
115 'Special:AllMyUploads',
116 ],
117 ] ],
118 [ [
119 'Special namespace with prefix',
120 'query' => 'Special:Un',
121 'results' => [
122 'Special:Unblock',
123 'Special:UncategorizedCategories',
124 'Special:UncategorizedFiles',
125 ],
126 // Third result when testing offset
127 'offsetresult' => [
128 'Special:UncategorizedImages',
129 ],
130 ] ],
131 [ [
132 'Special page name',
133 'query' => 'Special:EditWatchlist',
134 'results' => [
135 'Special:EditWatchlist',
136 ],
137 ] ],
138 [ [
139 'Special page subpages',
140 'query' => 'Special:EditWatchlist/',
141 'results' => [
142 'Special:EditWatchlist/clear',
143 'Special:EditWatchlist/raw',
144 ],
145 ] ],
146 [ [
147 'Special page subpages with prefix',
148 'query' => 'Special:EditWatchlist/cl',
149 'results' => [
150 'Special:EditWatchlist/clear',
151 ],
152 ] ],
153 ];
154 }
155
156 /**
157 * @dataProvider provideSearch
158 * @covers SearchEngine::defaultPrefixSearch
159 */
160 public function testSearch( array $case ) {
161 $this->search->setLimitOffset( 3 );
162 $results = $this->search->defaultPrefixSearch( $case['query'] );
163 $results = array_map( function( Title $t ) {
164 return $t->getPrefixedText();
165 }, $results );
166 $this->assertEquals(
167 $case['results'],
168 $results,
169 $case[0]
170 );
171 }
172
173 /**
174 * @dataProvider provideSearch
175 * @covers SearchEngine::defaultPrefixSearch
176 */
177 public function testSearchWithOffset( array $case ) {
178 $this->search->setLimitOffset( 3, 1 );
179 $results = $this->search->defaultPrefixSearch( $case['query'] );
180 $results = array_map( function( Title $t ) {
181 return $t->getPrefixedText();
182 }, $results );
183
184 // We don't expect the first result when offsetting
185 array_shift( $case['results'] );
186 // And sometimes we expect a different last result
187 $expected = isset( $case['offsetresult'] ) ?
188 array_merge( $case['results'], $case['offsetresult'] ) :
189 $case['results'];
190
191 $this->assertEquals(
192 $expected,
193 $results,
194 $case[0]
195 );
196 }
197
198 public static function provideSearchBackend() {
199 return [
200 [ [
201 'Simple case',
202 'provision' => [
203 'Bar',
204 'Barcelona',
205 'Barbara',
206 ],
207 'query' => 'Bar',
208 'results' => [
209 'Bar',
210 'Barcelona',
211 'Barbara',
212 ],
213 ] ],
214 [ [
215 'Exact match not on top (bug 70958)',
216 'provision' => [
217 'Barcelona',
218 'Bar',
219 'Barbara',
220 ],
221 'query' => 'Bar',
222 'results' => [
223 'Bar',
224 'Barcelona',
225 'Barbara',
226 ],
227 ] ],
228 [ [
229 'Exact match missing (bug 70958)',
230 'provision' => [
231 'Barcelona',
232 'Barbara',
233 'Bart',
234 ],
235 'query' => 'Bar',
236 'results' => [
237 'Bar',
238 'Barcelona',
239 'Barbara',
240 ],
241 ] ],
242 [ [
243 'Exact match missing and not existing',
244 'provision' => [
245 'Exile',
246 'Exist',
247 'External',
248 ],
249 'query' => 'Ex',
250 'results' => [
251 'Exile',
252 'Exist',
253 'External',
254 ],
255 ] ],
256 [ [
257 "Exact match shouldn't override already found match if " .
258 "exact is redirect and found isn't",
259 'provision' => [
260 // Target of the exact match is low in the list
261 'Redirect Test Worse Result',
262 'Redirect Test',
263 ],
264 'query' => 'redirect test',
265 'results' => [
266 // Redirect target is pulled up and exact match isn't added
267 'Redirect Test',
268 'Redirect Test Worse Result',
269 ],
270 ] ],
271 [ [
272 "Exact match shouldn't override already found match if " .
273 "both exact match and found match are redirect",
274 'provision' => [
275 // Another redirect to the same target as the exact match
276 // is low in the list
277 'Redirect Test2 Worse Result',
278 'Redirect test2',
279 ],
280 'query' => 'redirect TEST2',
281 'results' => [
282 // Found redirect is pulled to the top and exact match isn't
283 // added
284 'Redirect test2',
285 'Redirect Test2 Worse Result',
286 ],
287 ] ],
288 [ [
289 "Exact match should override any already found matches that " .
290 "are redirects to it",
291 'provision' => [
292 // Another redirect to the same target as the exact match
293 // is low in the list
294 'Redirect Test Worse Result',
295 'Redirect test',
296 ],
297 'query' => 'Redirect Test',
298 'results' => [
299 // Found redirect is pulled to the top and exact match isn't
300 // added
301 'Redirect Test',
302 'Redirect Test Worse Result',
303 'Redirect test',
304 ],
305 ] ],
306 ];
307 }
308
309 /**
310 * @dataProvider provideSearchBackend
311 * @covers PrefixSearch::searchBackend
312 */
313 public function testSearchBackend( array $case ) {
314 $search = $stub = $this->getMockBuilder( 'SearchEngine' )
315 ->setMethods( [ 'completionSearchBackend' ] )->getMock();
316
317 $return = SearchSuggestionSet::fromStrings( $case['provision'] );
318
319 $search->expects( $this->any() )
320 ->method( 'completionSearchBackend' )
321 ->will( $this->returnValue( $return ) );
322
323 $search->setLimitOffset( 3 );
324 $results = $search->completionSearch( $case['query'] );
325
326 $results = $results->map( function( SearchSuggestion $s ) {
327 return $s->getText();
328 } );
329
330 $this->assertEquals(
331 $case['results'],
332 $results,
333 $case[0]
334 );
335 }
336 }