f20756425f09c06a8af782bf258e04142528f05c
[lhc/web/wiklou.git] / tests / phpunit / includes / changetags / ChangeTagsTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4
5 /**
6 * @covers ChangeTags
7 * @group Database
8 */
9 class ChangeTagsTest extends MediaWikiTestCase {
10
11 public function setUp() {
12 parent::setUp();
13
14 $this->tablesUsed[] = 'change_tag';
15 $this->tablesUsed[] = 'change_tag_def';
16 $this->tablesUsed[] = 'tag_summary';
17 $this->tablesUsed[] = 'valid_tag';
18
19 // Truncate these to avoid the supposed-to-be-unused IDs in tests here turning
20 // out to be used, leading ChangeTags::updateTags() to pick up bogus rc_id,
21 // log_id, or rev_id values and run into unique constraint violations.
22 $this->tablesUsed[] = 'recentchanges';
23 $this->tablesUsed[] = 'logging';
24 $this->tablesUsed[] = 'revision';
25 $this->tablesUsed[] = 'archive';
26 }
27
28 // TODO only modifyDisplayQuery and getSoftwareTags are tested, nothing else is
29
30 /** @dataProvider provideModifyDisplayQuery */
31 public function testModifyDisplayQuery( $origQuery, $filter_tag, $useTags, $modifiedQuery ) {
32 $this->setMwGlobals( 'wgUseTagFilter', $useTags );
33 // HACK resolve deferred group concats (see comment in provideModifyDisplayQuery)
34 if ( isset( $modifiedQuery['fields']['ts_tags'] ) ) {
35 $modifiedQuery['fields']['ts_tags'] = call_user_func_array(
36 [ wfGetDB( DB_REPLICA ), 'buildGroupConcatField' ],
37 $modifiedQuery['fields']['ts_tags']
38 );
39 }
40 if ( isset( $modifiedQuery['exception'] ) ) {
41 $this->setExpectedException( $modifiedQuery['exception'] );
42 }
43 ChangeTags::modifyDisplayQuery(
44 $origQuery['tables'],
45 $origQuery['fields'],
46 $origQuery['conds'],
47 $origQuery['join_conds'],
48 $origQuery['options'],
49 $filter_tag
50 );
51 if ( !isset( $modifiedQuery['exception'] ) ) {
52 $this->assertArrayEquals(
53 $modifiedQuery,
54 $origQuery,
55 /* ordered = */ false,
56 /* named = */ true
57 );
58 }
59 }
60
61 public function provideModifyDisplayQuery() {
62 // HACK if we call $dbr->buildGroupConcatField() now, it will return the wrong table names
63 // We have to have the test runner call it instead
64 $groupConcats = [
65 'recentchanges' => [ ',', 'change_tag', 'ct_tag', 'ct_rc_id=rc_id' ],
66 'logging' => [ ',', 'change_tag', 'ct_tag', 'ct_log_id=log_id' ],
67 'revision' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=rev_id' ],
68 'archive' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=ar_rev_id' ],
69 ];
70
71 return [
72 'simple recentchanges query' => [
73 [
74 'tables' => [ 'recentchanges' ],
75 'fields' => [ 'rc_id', 'rc_timestamp' ],
76 'conds' => [ "rc_timestamp > '20170714183203'" ],
77 'join_conds' => [],
78 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
79 ],
80 '', // no tag filter
81 true, // tag filtering enabled
82 [
83 'tables' => [ 'recentchanges' ],
84 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
85 'conds' => [ "rc_timestamp > '20170714183203'" ],
86 'join_conds' => [],
87 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
88 ]
89 ],
90 'simple query with strings' => [
91 [
92 'tables' => 'recentchanges',
93 'fields' => 'rc_id',
94 'conds' => "rc_timestamp > '20170714183203'",
95 'join_conds' => [],
96 'options' => 'ORDER BY rc_timestamp DESC',
97 ],
98 '', // no tag filter
99 true, // tag filtering enabled
100 [
101 'tables' => [ 'recentchanges' ],
102 'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
103 'conds' => [ "rc_timestamp > '20170714183203'" ],
104 'join_conds' => [],
105 'options' => [ 'ORDER BY rc_timestamp DESC' ],
106 ]
107 ],
108 'recentchanges query with single tag filter' => [
109 [
110 'tables' => [ 'recentchanges' ],
111 'fields' => [ 'rc_id', 'rc_timestamp' ],
112 'conds' => [ "rc_timestamp > '20170714183203'" ],
113 'join_conds' => [],
114 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
115 ],
116 'foo',
117 true, // tag filtering enabled
118 [
119 'tables' => [ 'recentchanges', 'change_tag' ],
120 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
121 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
122 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
123 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
124 ]
125 ],
126 'logging query with single tag filter and strings' => [
127 [
128 'tables' => 'logging',
129 'fields' => 'log_id',
130 'conds' => "log_timestamp > '20170714183203'",
131 'join_conds' => [],
132 'options' => 'ORDER BY log_timestamp DESC',
133 ],
134 'foo',
135 true, // tag filtering enabled
136 [
137 'tables' => [ 'logging', 'change_tag' ],
138 'fields' => [ 'log_id', 'ts_tags' => $groupConcats['logging'] ],
139 'conds' => [ "log_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
140 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_log_id=log_id' ] ],
141 'options' => [ 'ORDER BY log_timestamp DESC' ],
142 ]
143 ],
144 'revision query with single tag filter' => [
145 [
146 'tables' => [ 'revision' ],
147 'fields' => [ 'rev_id', 'rev_timestamp' ],
148 'conds' => [ "rev_timestamp > '20170714183203'" ],
149 'join_conds' => [],
150 'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
151 ],
152 'foo',
153 true, // tag filtering enabled
154 [
155 'tables' => [ 'revision', 'change_tag' ],
156 'fields' => [ 'rev_id', 'rev_timestamp', 'ts_tags' => $groupConcats['revision'] ],
157 'conds' => [ "rev_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
158 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=rev_id' ] ],
159 'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
160 ]
161 ],
162 'archive query with single tag filter' => [
163 [
164 'tables' => [ 'archive' ],
165 'fields' => [ 'ar_id', 'ar_timestamp' ],
166 'conds' => [ "ar_timestamp > '20170714183203'" ],
167 'join_conds' => [],
168 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
169 ],
170 'foo',
171 true, // tag filtering enabled
172 [
173 'tables' => [ 'archive', 'change_tag' ],
174 'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
175 'conds' => [ "ar_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
176 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=ar_rev_id' ] ],
177 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
178 ]
179 ],
180 'unsupported table name throws exception (even without tag filter)' => [
181 [
182 'tables' => [ 'foobar' ],
183 'fields' => [ 'fb_id', 'fb_timestamp' ],
184 'conds' => [ "fb_timestamp > '20170714183203'" ],
185 'join_conds' => [],
186 'options' => [ 'ORDER BY' => 'fb_timestamp DESC' ],
187 ],
188 '',
189 true, // tag filtering enabled
190 [ 'exception' => MWException::class ]
191 ],
192 'tag filter ignored when tag filtering is disabled' => [
193 [
194 'tables' => [ 'archive' ],
195 'fields' => [ 'ar_id', 'ar_timestamp' ],
196 'conds' => [ "ar_timestamp > '20170714183203'" ],
197 'join_conds' => [],
198 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
199 ],
200 'foo',
201 false, // tag filtering disabled
202 [
203 'tables' => [ 'archive' ],
204 'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
205 'conds' => [ "ar_timestamp > '20170714183203'" ],
206 'join_conds' => [],
207 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
208 ]
209 ],
210 'recentchanges query with multiple tag filter' => [
211 [
212 'tables' => [ 'recentchanges' ],
213 'fields' => [ 'rc_id', 'rc_timestamp' ],
214 'conds' => [ "rc_timestamp > '20170714183203'" ],
215 'join_conds' => [],
216 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
217 ],
218 [ 'foo', 'bar' ],
219 true, // tag filtering enabled
220 [
221 'tables' => [ 'recentchanges', 'change_tag' ],
222 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
223 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
224 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
225 'options' => [ 'ORDER BY' => 'rc_timestamp DESC', 'DISTINCT' ],
226 ]
227 ],
228 'recentchanges query with multiple tag filter that already has DISTINCT' => [
229 [
230 'tables' => [ 'recentchanges' ],
231 'fields' => [ 'rc_id', 'rc_timestamp' ],
232 'conds' => [ "rc_timestamp > '20170714183203'" ],
233 'join_conds' => [],
234 'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
235 ],
236 [ 'foo', 'bar' ],
237 true, // tag filtering enabled
238 [
239 'tables' => [ 'recentchanges', 'change_tag' ],
240 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
241 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
242 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
243 'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
244 ]
245 ],
246 'recentchanges query with multiple tag filter with strings' => [
247 [
248 'tables' => 'recentchanges',
249 'fields' => 'rc_id',
250 'conds' => "rc_timestamp > '20170714183203'",
251 'join_conds' => [],
252 'options' => 'ORDER BY rc_timestamp DESC',
253 ],
254 [ 'foo', 'bar' ],
255 true, // tag filtering enabled
256 [
257 'tables' => [ 'recentchanges', 'change_tag' ],
258 'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
259 'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
260 'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
261 'options' => [ 'ORDER BY rc_timestamp DESC', 'DISTINCT' ],
262 ]
263 ],
264 ];
265 }
266
267 public static function dataGetSoftwareTags() {
268 return [
269 [
270 [
271 'mw-contentModelChange' => true,
272 'mw-redirect' => true,
273 'mw-rollback' => true,
274 'mw-blank' => true,
275 'mw-replace' => true
276 ],
277 [
278 'mw-rollback',
279 'mw-replace',
280 'mw-blank'
281 ]
282 ],
283
284 [
285 [
286 'mw-contentmodelchanged' => true,
287 'mw-replace' => true,
288 'mw-new-redirects' => true,
289 'mw-changed-redirect-target' => true,
290 'mw-rolback' => true,
291 'mw-blanking' => false
292 ],
293 [
294 'mw-replace',
295 'mw-changed-redirect-target'
296 ]
297 ],
298
299 [
300 [
301 null,
302 false,
303 'Lorem ipsum',
304 'mw-translation'
305 ],
306 []
307 ],
308
309 [
310 [],
311 []
312 ]
313 ];
314 }
315
316 /**
317 * @dataProvider dataGetSoftwareTags
318 * @covers ChangeTags::getSoftwareTags
319 */
320 public function testGetSoftwareTags( $softwareTags, $expected ) {
321 $this->setMwGlobals( 'wgSoftwareTags', $softwareTags );
322
323 $actual = ChangeTags::getSoftwareTags();
324 // Order of tags in arrays is not important
325 sort( $expected );
326 sort( $actual );
327 $this->assertEquals( $expected, $actual );
328 }
329
330 public function testUpdateTagsMigrationOld() {
331 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_OLD );
332 $dbw = wfGetDB( DB_MASTER );
333 $dbw->delete( 'change_tag', '*' );
334 $dbw->delete( 'change_tag_def', '*' );
335
336 $rcId = 123;
337 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
338
339 $dbr = wfGetDB( DB_REPLICA );
340
341 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
342 $this->assertEquals( [], iterator_to_array( $res, false ) );
343
344 $expected2 = [
345 (object)[
346 'ct_tag' => 'tag1',
347 'ct_tag_id' => null,
348 'ct_rc_id' => 123
349 ],
350 (object)[
351 'ct_tag' => 'tag2',
352 'ct_tag_id' => null,
353 'ct_rc_id' => 123
354 ],
355 ];
356 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
357 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
358
359 $rcId = 124;
360 ChangeTags::updateTags( [ 'tag1', 'tag3' ], [], $rcId );
361
362 $dbr = wfGetDB( DB_REPLICA );
363
364 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
365 $this->assertEquals( [], iterator_to_array( $res, false ) );
366
367 $expected2 = [
368 (object)[
369 'ct_tag' => 'tag1',
370 'ct_tag_id' => null,
371 'ct_rc_id' => 123
372 ],
373 (object)[
374 'ct_tag' => 'tag2',
375 'ct_tag_id' => null,
376 'ct_rc_id' => 123
377 ],
378 (object)[
379 'ct_tag' => 'tag1',
380 'ct_tag_id' => null,
381 'ct_rc_id' => 124
382 ],
383 (object)[
384 'ct_tag' => 'tag3',
385 'ct_tag_id' => null,
386 'ct_rc_id' => 124
387 ],
388 ];
389 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
390 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
391 }
392
393 public function testUpdateTagsMigrationWriteBoth() {
394 // FIXME: fails under postgres
395 $this->markTestSkippedIfDbType( 'postgres' );
396
397 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
398 $dbw = wfGetDB( DB_MASTER );
399 $dbw->delete( 'change_tag', '*' );
400 $dbw->delete( 'change_tag_def', '*' );
401
402 $rcId = 123;
403 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
404
405 $dbr = wfGetDB( DB_REPLICA );
406
407 $expected = [
408 (object)[
409 'ctd_name' => 'tag1',
410 'ctd_id' => 1,
411 'ctd_count' => 1
412 ],
413 (object)[
414 'ctd_name' => 'tag2',
415 'ctd_id' => 2,
416 'ctd_count' => 1
417 ],
418 ];
419 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
420 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
421
422 $expected2 = [
423 (object)[
424 'ct_tag' => 'tag1',
425 'ct_tag_id' => 1,
426 'ct_rc_id' => 123
427 ],
428 (object)[
429 'ct_tag' => 'tag2',
430 'ct_tag_id' => 2,
431 'ct_rc_id' => 123
432 ],
433 ];
434 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
435 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
436
437 $rcId = 124;
438 ChangeTags::updateTags( [ 'tag1' ], [], $rcId );
439
440 ChangeTags::updateTags( [ 'tag3' ], [], $rcId );
441
442 $dbr = wfGetDB( DB_REPLICA );
443
444 $expected = [
445 (object)[
446 'ctd_name' => 'tag1',
447 'ctd_id' => 1,
448 'ctd_count' => 2
449 ],
450 (object)[
451 'ctd_name' => 'tag2',
452 'ctd_id' => 2,
453 'ctd_count' => 1
454 ],
455 (object)[
456 'ctd_name' => 'tag3',
457 'ctd_id' => 3,
458 'ctd_count' => 1
459 ],
460 ];
461 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
462 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
463
464 $expected2 = [
465 (object)[
466 'ct_tag' => 'tag1',
467 'ct_tag_id' => 1,
468 'ct_rc_id' => 123
469 ],
470 (object)[
471 'ct_tag' => 'tag2',
472 'ct_tag_id' => 2,
473 'ct_rc_id' => 123
474 ],
475 (object)[
476 'ct_tag' => 'tag1',
477 'ct_tag_id' => 1,
478 'ct_rc_id' => 124
479 ],
480 (object)[
481 'ct_tag' => 'tag3',
482 'ct_tag_id' => 3,
483 'ct_rc_id' => 124
484 ],
485 ];
486 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
487 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
488 }
489
490 public function testDeleteTagsMigrationOld() {
491 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_OLD );
492 $dbw = wfGetDB( DB_MASTER );
493 $dbw->delete( 'change_tag', '*' );
494 $dbw->delete( 'change_tag_def', '*' );
495
496 $rcId = 123;
497 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
498
499 ChangeTags::updateTags( [], [ 'tag2' ], $rcId );
500
501 $dbr = wfGetDB( DB_REPLICA );
502
503 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
504 $this->assertEquals( [], iterator_to_array( $res, false ) );
505
506 $expected2 = [
507 (object)[
508 'ct_tag' => 'tag1',
509 'ct_tag_id' => null,
510 'ct_rc_id' => 123
511 ]
512 ];
513 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
514 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
515 }
516
517 public function testDeleteTagsMigrationWriteBoth() {
518 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
519 $dbw = wfGetDB( DB_MASTER );
520 $dbw->delete( 'change_tag', '*' );
521 $dbw->delete( 'change_tag_def', '*' );
522 MediaWikiServices::getInstance()->resetServiceForTesting( 'NameTableStoreFactory' );
523
524 $rcId = 123;
525 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
526
527 ChangeTags::updateTags( [], [ 'tag2' ], $rcId );
528
529 $dbr = wfGetDB( DB_REPLICA );
530
531 $expected = [
532 (object)[
533 'ctd_name' => 'tag1',
534 'ctd_id' => 1,
535 'ctd_count' => 1
536 ],
537 ];
538 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
539 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
540
541 $expected2 = [
542 (object)[
543 'ct_tag' => 'tag1',
544 'ct_tag_id' => 1,
545 'ct_rc_id' => 123
546 ]
547 ];
548 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
549 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
550 }
551
552 public function testTagUsageStatisticsOldBackend() {
553 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_OLD );
554 $this->setMwGlobals( 'wgTagStatisticsNewTable', false );
555
556 $dbw = wfGetDB( DB_MASTER );
557 $dbw->delete( 'change_tag', '*' );
558 $dbw->delete( 'change_tag_def', '*' );
559
560 $rcId = 123;
561 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
562
563 $rcId = 124;
564 ChangeTags::updateTags( [ 'tag1' ], [], $rcId );
565
566 $this->assertEquals( [ 'tag1' => 2, 'tag2' => 1 ], ChangeTags::tagUsageStatistics() );
567 }
568
569 public function testTagUsageStatisticsNewMigrationOldBackedn() {
570 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
571 $this->setMwGlobals( 'wgTagStatisticsNewTable', false );
572
573 $dbw = wfGetDB( DB_MASTER );
574 $dbw->delete( 'change_tag', '*' );
575 $dbw->delete( 'change_tag_def', '*' );
576 MediaWikiServices::getInstance()->resetServiceForTesting( 'NameTableStoreFactory' );
577
578 $rcId = 123;
579 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
580
581 $rcId = 124;
582 ChangeTags::updateTags( [ 'tag1' ], [], $rcId );
583
584 $this->assertEquals( [ 'tag1' => 2, 'tag2' => 1 ], ChangeTags::tagUsageStatistics() );
585 }
586
587 public function testTagUsageStatisticsNewBackend() {
588 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
589 $this->setMwGlobals( 'wgTagStatisticsNewTable', true );
590
591 $dbw = wfGetDB( DB_MASTER );
592 $dbw->delete( 'change_tag', '*' );
593 $dbw->delete( 'change_tag_def', '*' );
594 MediaWikiServices::getInstance()->resetServiceForTesting( 'NameTableStoreFactory' );
595
596 $rcId = 123;
597 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
598
599 $rcId = 124;
600 ChangeTags::updateTags( [ 'tag1' ], [], $rcId );
601
602 $this->assertEquals( [ 'tag1' => 2, 'tag2' => 1 ], ChangeTags::tagUsageStatistics() );
603 }
604
605 public function testListExplicitlyDefinedTagsOld() {
606 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_OLD );
607 $dbw = wfGetDB( DB_MASTER );
608 $dbw->delete( 'change_tag', '*' );
609 $dbw->delete( 'change_tag_def', '*' );
610 $dbw->delete( 'valid_tag', '*' );
611
612 $rcId = 123;
613 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
614 ChangeTags::defineTag( 'tag2' );
615
616 $this->assertEquals( [ 'tag2' ], ChangeTags::listExplicitlyDefinedTags() );
617 $dbr = wfGetDB( DB_REPLICA );
618 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_user_defined' ], '' );
619 $this->assertEquals( [], iterator_to_array( $res, false ) );
620
621 $this->assertEquals( [ 'tag2' ], $dbr->selectFieldValues( 'valid_tag', 'vt_tag', '' ) );
622 }
623
624 public function testListExplicitlyDefinedTagsWriteBoth() {
625 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
626 $dbw = wfGetDB( DB_MASTER );
627 $dbw->delete( 'change_tag', '*' );
628 $dbw->delete( 'change_tag_def', '*' );
629 $dbw->delete( 'valid_tag', '*' );
630
631 $rcId = 123;
632 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
633 ChangeTags::defineTag( 'tag2' );
634
635 $this->assertEquals( [ 'tag2' ], ChangeTags::listExplicitlyDefinedTags() );
636 $dbr = wfGetDB( DB_REPLICA );
637
638 $expected = [
639 (object)[
640 'ctd_name' => 'tag1',
641 'ctd_user_defined' => 0
642 ],
643 (object)[
644 'ctd_name' => 'tag2',
645 'ctd_user_defined' => 1
646 ],
647 ];
648 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_user_defined' ], '' );
649 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
650
651 $this->assertEquals( [ 'tag2' ], $dbr->selectFieldValues( 'valid_tag', 'vt_tag', '' ) );
652 }
653
654 public function testListExplicitlyDefinedTagsNew() {
655 $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_NEW );
656 $dbw = wfGetDB( DB_MASTER );
657 $dbw->delete( 'change_tag', '*' );
658 $dbw->delete( 'change_tag_def', '*' );
659 $dbw->delete( 'valid_tag', '*' );
660
661 $rcId = 123;
662 ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
663 ChangeTags::defineTag( 'tag2' );
664
665 $this->assertEquals( [ 'tag2' ], ChangeTags::listExplicitlyDefinedTags() );
666 $dbr = wfGetDB( DB_REPLICA );
667
668 $expected = [
669 (object)[
670 'ctd_name' => 'tag1',
671 'ctd_user_defined' => 0
672 ],
673 (object)[
674 'ctd_name' => 'tag2',
675 'ctd_user_defined' => 1
676 ],
677 ];
678 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_user_defined' ], '' );
679 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
680
681 $this->assertEquals( [], $dbr->selectFieldValues( 'valid_tag', 'vt_tag', '' ) );
682 }
683 }