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