Add code to write to change_tag_def table
[lhc/web/wiklou.git] / tests / phpunit / includes / changetags / ChangeTagsTest.php
1 <?php
2
3 /**
4 * @covers ChangeTags
5 * @group Database
6 */
7 class ChangeTagsTest extends MediaWikiTestCase {
8
9 public function setUp() {
10 parent::setUp();
11
12 $this->tablesUsed[] = 'change_tag';
13 $this->tablesUsed[] = 'change_tag_def';
14 $this->tablesUsed[] = 'tag_summary';
15 }
16
17 // TODO only modifyDisplayQuery and getSoftwareTags are tested, nothing else is
18
19 /** @dataProvider provideModifyDisplayQuery */
20 public function testModifyDisplayQuery( $origQuery, $filter_tag, $useTags, $modifiedQuery ) {
21 $this->setMwGlobals( 'wgUseTagFilter', $useTags );
22 // HACK resolve deferred group concats (see comment in provideModifyDisplayQuery)
23 if ( isset( $modifiedQuery['fields']['ts_tags'] ) ) {
24 $modifiedQuery['fields']['ts_tags'] = call_user_func_array(
25 [ wfGetDB( DB_REPLICA ), 'buildGroupConcatField' ],
26 $modifiedQuery['fields']['ts_tags']
27 );
28 }
29 if ( isset( $modifiedQuery['exception'] ) ) {
30 $this->setExpectedException( $modifiedQuery['exception'] );
31 }
32 ChangeTags::modifyDisplayQuery(
33 $origQuery['tables'],
34 $origQuery['fields'],
35 $origQuery['conds'],
36 $origQuery['join_conds'],
37 $origQuery['options'],
38 $filter_tag
39 );
40 if ( !isset( $modifiedQuery['exception'] ) ) {
41 $this->assertArrayEquals(
42 $modifiedQuery,
43 $origQuery,
44 /* ordered = */ false,
45 /* named = */ true
46 );
47 }
48 }
49
50 public function provideModifyDisplayQuery() {
51 // HACK if we call $dbr->buildGroupConcatField() now, it will return the wrong table names
52 // We have to have the test runner call it instead
53 $groupConcats = [
54 'recentchanges' => [ ',', 'change_tag', 'ct_tag', 'ct_rc_id=rc_id' ],
55 'logging' => [ ',', 'change_tag', 'ct_tag', 'ct_log_id=log_id' ],
56 'revision' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=rev_id' ],
57 'archive' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=ar_rev_id' ],
58 ];
59
60 return [
61 'simple recentchanges query' => [
62 [
63 'tables' => [ 'recentchanges' ],
64 'fields' => [ 'rc_id', 'rc_timestamp' ],
65 'conds' => [ "rc_timestamp > '20170714183203'" ],
66 'join_conds' => [],
67 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
68 ],
69 '', // no tag filter
70 true, // tag filtering enabled
71 [
72 'tables' => [ 'recentchanges' ],
73 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
74 'conds' => [ "rc_timestamp > '20170714183203'" ],
75 'join_conds' => [],
76 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
77 ]
78 ],
79 'simple query with strings' => [
80 [
81 'tables' => 'recentchanges',
82 'fields' => 'rc_id',
83 'conds' => "rc_timestamp > '20170714183203'",
84 'join_conds' => [],
85 'options' => 'ORDER BY rc_timestamp DESC',
86 ],
87 '', // no tag filter
88 true, // tag filtering enabled
89 [
90 'tables' => [ 'recentchanges' ],
91 'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
92 'conds' => [ "rc_timestamp > '20170714183203'" ],
93 'join_conds' => [],
94 'options' => [ 'ORDER BY rc_timestamp DESC' ],
95 ]
96 ],
97 'recentchanges query with single tag filter' => [
98 [
99 'tables' => [ 'recentchanges' ],
100 'fields' => [ 'rc_id', 'rc_timestamp' ],
101 'conds' => [ "rc_timestamp > '20170714183203'" ],
102 'join_conds' => [],
103 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
104 ],
105 'foo',
106 true, // tag filtering enabled
107 [
108 'tables' => [ 'recentchanges', 'change_tag' ],
109 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
110 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
111 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
112 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
113 ]
114 ],
115 'logging query with single tag filter and strings' => [
116 [
117 'tables' => 'logging',
118 'fields' => 'log_id',
119 'conds' => "log_timestamp > '20170714183203'",
120 'join_conds' => [],
121 'options' => 'ORDER BY log_timestamp DESC',
122 ],
123 'foo',
124 true, // tag filtering enabled
125 [
126 'tables' => [ 'logging', 'change_tag' ],
127 'fields' => [ 'log_id', 'ts_tags' => $groupConcats['logging'] ],
128 'conds' => [ "log_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
129 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_log_id=log_id' ] ],
130 'options' => [ 'ORDER BY log_timestamp DESC' ],
131 ]
132 ],
133 'revision query with single tag filter' => [
134 [
135 'tables' => [ 'revision' ],
136 'fields' => [ 'rev_id', 'rev_timestamp' ],
137 'conds' => [ "rev_timestamp > '20170714183203'" ],
138 'join_conds' => [],
139 'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
140 ],
141 'foo',
142 true, // tag filtering enabled
143 [
144 'tables' => [ 'revision', 'change_tag' ],
145 'fields' => [ 'rev_id', 'rev_timestamp', 'ts_tags' => $groupConcats['revision'] ],
146 'conds' => [ "rev_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
147 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=rev_id' ] ],
148 'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
149 ]
150 ],
151 'archive query with single tag filter' => [
152 [
153 'tables' => [ 'archive' ],
154 'fields' => [ 'ar_id', 'ar_timestamp' ],
155 'conds' => [ "ar_timestamp > '20170714183203'" ],
156 'join_conds' => [],
157 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
158 ],
159 'foo',
160 true, // tag filtering enabled
161 [
162 'tables' => [ 'archive', 'change_tag' ],
163 'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
164 'conds' => [ "ar_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
165 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=ar_rev_id' ] ],
166 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
167 ]
168 ],
169 'unsupported table name throws exception (even without tag filter)' => [
170 [
171 'tables' => [ 'foobar' ],
172 'fields' => [ 'fb_id', 'fb_timestamp' ],
173 'conds' => [ "fb_timestamp > '20170714183203'" ],
174 'join_conds' => [],
175 'options' => [ 'ORDER BY' => 'fb_timestamp DESC' ],
176 ],
177 '',
178 true, // tag filtering enabled
179 [ 'exception' => MWException::class ]
180 ],
181 'tag filter ignored when tag filtering is disabled' => [
182 [
183 'tables' => [ 'archive' ],
184 'fields' => [ 'ar_id', 'ar_timestamp' ],
185 'conds' => [ "ar_timestamp > '20170714183203'" ],
186 'join_conds' => [],
187 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
188 ],
189 'foo',
190 false, // tag filtering disabled
191 [
192 'tables' => [ 'archive' ],
193 'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
194 'conds' => [ "ar_timestamp > '20170714183203'" ],
195 'join_conds' => [],
196 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
197 ]
198 ],
199 'recentchanges query with multiple tag filter' => [
200 [
201 'tables' => [ 'recentchanges' ],
202 'fields' => [ 'rc_id', 'rc_timestamp' ],
203 'conds' => [ "rc_timestamp > '20170714183203'" ],
204 'join_conds' => [],
205 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
206 ],
207 [ 'foo', 'bar' ],
208 true, // tag filtering enabled
209 [
210 'tables' => [ 'recentchanges', 'change_tag' ],
211 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
212 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
213 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
214 'options' => [ 'ORDER BY' => 'rc_timestamp DESC', 'DISTINCT' ],
215 ]
216 ],
217 'recentchanges query with multiple tag filter that already has DISTINCT' => [
218 [
219 'tables' => [ 'recentchanges' ],
220 'fields' => [ 'rc_id', 'rc_timestamp' ],
221 'conds' => [ "rc_timestamp > '20170714183203'" ],
222 'join_conds' => [],
223 'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
224 ],
225 [ 'foo', 'bar' ],
226 true, // tag filtering enabled
227 [
228 'tables' => [ 'recentchanges', 'change_tag' ],
229 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
230 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
231 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
232 'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
233 ]
234 ],
235 'recentchanges query with multiple tag filter with strings' => [
236 [
237 'tables' => 'recentchanges',
238 'fields' => 'rc_id',
239 'conds' => "rc_timestamp > '20170714183203'",
240 'join_conds' => [],
241 'options' => 'ORDER BY rc_timestamp DESC',
242 ],
243 [ 'foo', 'bar' ],
244 true, // tag filtering enabled
245 [
246 'tables' => [ 'recentchanges', 'change_tag' ],
247 'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
248 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
249 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
250 'options' => [ 'ORDER BY rc_timestamp DESC', 'DISTINCT' ],
251 ]
252 ],
253 ];
254 }
255
256 public static function dataGetSoftwareTags() {
257 return [
258 [
259 [
260 'mw-contentModelChange' => true,
261 'mw-redirect' => true,
262 'mw-rollback' => true,
263 'mw-blank' => true,
264 'mw-replace' => true
265 ],
266 [
267 'mw-rollback',
268 'mw-replace',
269 'mw-blank'
270 ]
271 ],
272
273 [
274 [
275 'mw-contentmodelchanged' => true,
276 'mw-replace' => true,
277 'mw-new-redirects' => true,
278 'mw-changed-redirect-target' => true,
279 'mw-rolback' => true,
280 'mw-blanking' => false
281 ],
282 [
283 'mw-replace',
284 'mw-changed-redirect-target'
285 ]
286 ],
287
288 [
289 [
290 null,
291 false,
292 'Lorem ipsum',
293 'mw-translation'
294 ],
295 []
296 ],
297
298 [
299 [],
300 []
301 ]
302 ];
303 }
304
305 /**
306 * @dataProvider dataGetSoftwareTags
307 * @covers ChangeTags::getSoftwareTags
308 */
309 public function testGetSoftwareTags( $softwareTags, $expected ) {
310 $this->setMwGlobals( 'wgSoftwareTags', $softwareTags );
311
312 $actual = ChangeTags::getSoftwareTags();
313 // Order of tags in arrays is not important
314 sort( $expected );
315 sort( $actual );
316 $this->assertEquals( $expected, $actual );
317 }
318
319 public function testUpdateTagsMigrationOld() {
320 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_OLD );
321 $dbw = wfGetDB( DB_MASTER );
322 $dbw->delete( 'change_tag', '*' );
323 $dbw->delete( 'change_tag_def', '*' );
324
325 $rcId = 123;
326 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
327
328 $dbr = wfGetDB( DB_REPLICA );
329
330 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
331 $this->assertEquals( [], iterator_to_array( $res, false ) );
332
333 $expected2 = [
334 (object)[
335 'ct_tag' => 'tag1',
336 'ct_tag_id' => null,
337 'ct_rc_id' => 123
338 ],
339 (object)[
340 'ct_tag' => 'tag2',
341 'ct_tag_id' => null,
342 'ct_rc_id' => 123
343 ],
344 ];
345 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
346 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
347
348 $rcId = 124;
349 ChangeTags::updateTags( [ 'tag1', 'tag3' ], [], $rcId );
350
351 $dbr = wfGetDB( DB_REPLICA );
352
353 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
354 $this->assertEquals( [], iterator_to_array( $res, false ) );
355
356 $expected2 = [
357 (object)[
358 'ct_tag' => 'tag1',
359 'ct_tag_id' => null,
360 'ct_rc_id' => 123
361 ],
362 (object)[
363 'ct_tag' => 'tag2',
364 'ct_tag_id' => null,
365 'ct_rc_id' => 123
366 ],
367 (object)[
368 'ct_tag' => 'tag1',
369 'ct_tag_id' => null,
370 'ct_rc_id' => 124
371 ],
372 (object)[
373 'ct_tag' => 'tag3',
374 'ct_tag_id' => null,
375 'ct_rc_id' => 124
376 ],
377 ];
378 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
379 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
380 }
381
382 public function testUpdateTagsMigrationWriteBoth() {
383 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
384 $dbw = wfGetDB( DB_MASTER );
385 $dbw->delete( 'change_tag', '*' );
386 $dbw->delete( 'change_tag_def', '*' );
387
388 $rcId = 123;
389 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
390
391 $dbr = wfGetDB( DB_REPLICA );
392
393 $expected = [
394 (object)[
395 'ctd_name' => 'tag1',
396 'ctd_id' => 1,
397 'ctd_count' => 1
398 ],
399 (object)[
400 'ctd_name' => 'tag2',
401 'ctd_id' => 2,
402 'ctd_count' => 1
403 ],
404 ];
405 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
406 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
407
408 $expected2 = [
409 (object)[
410 'ct_tag' => 'tag1',
411 'ct_tag_id' => 1,
412 'ct_rc_id' => 123
413 ],
414 (object)[
415 'ct_tag' => 'tag2',
416 'ct_tag_id' => 2,
417 'ct_rc_id' => 123
418 ],
419 ];
420 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
421 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
422
423 $rcId = 124;
424 ChangeTags::updateTags( [ 'tag1', 'tag3' ], [], $rcId );
425
426 $dbr = wfGetDB( DB_REPLICA );
427
428 $expected = [
429 (object)[
430 'ctd_name' => 'tag1',
431 'ctd_id' => 1,
432 'ctd_count' => 2
433 ],
434 (object)[
435 'ctd_name' => 'tag2',
436 'ctd_id' => 2,
437 'ctd_count' => 1
438 ],
439 (object)[
440 'ctd_name' => 'tag3',
441 'ctd_id' => 3,
442 'ctd_count' => 1
443 ],
444 ];
445 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
446 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
447
448 $expected2 = [
449 (object)[
450 'ct_tag' => 'tag1',
451 'ct_tag_id' => 1,
452 'ct_rc_id' => 123
453 ],
454 (object)[
455 'ct_tag' => 'tag2',
456 'ct_tag_id' => 2,
457 'ct_rc_id' => 123
458 ],
459 (object)[
460 'ct_tag' => 'tag1',
461 'ct_tag_id' => 1,
462 'ct_rc_id' => 124
463 ],
464 (object)[
465 'ct_tag' => 'tag3',
466 'ct_tag_id' => 3,
467 'ct_rc_id' => 124
468 ],
469 ];
470 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
471 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
472 }
473
474 public function testDeleteTagsMigrationOld() {
475 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_OLD );
476 $dbw = wfGetDB( DB_MASTER );
477 $dbw->delete( 'change_tag', '*' );
478 $dbw->delete( 'change_tag_def', '*' );
479
480 $rcId = 123;
481 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
482
483 ChangeTags::updateTags( [], [ 'tag2' ], $rcId );
484
485 $dbr = wfGetDB( DB_REPLICA );
486
487 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
488 $this->assertEquals( [], iterator_to_array( $res, false ) );
489
490 $expected2 = [
491 (object)[
492 'ct_tag' => 'tag1',
493 'ct_tag_id' => null,
494 'ct_rc_id' => 123
495 ]
496 ];
497 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
498 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
499 }
500
501 public function testDeleteTagsMigrationWriteBoth() {
502 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
503 $dbw = wfGetDB( DB_MASTER );
504 $dbw->delete( 'change_tag', '*' );
505 $dbw->delete( 'change_tag_def', '*' );
506
507 $rcId = 123;
508 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
509
510 ChangeTags::updateTags( [], [ 'tag2' ], $rcId );
511
512 $dbr = wfGetDB( DB_REPLICA );
513
514 $expected = [
515 (object)[
516 'ctd_name' => 'tag1',
517 'ctd_id' => 1,
518 'ctd_count' => 1
519 ],
520 ];
521 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
522 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
523
524 $expected2 = [
525 (object)[
526 'ct_tag' => 'tag1',
527 'ct_tag_id' => 1,
528 'ct_rc_id' => 123
529 ]
530 ];
531 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
532 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
533 }
534
535 }