7 class ChangeTagsTest
extends MediaWikiTestCase
{
9 public function setUp() {
12 $this->tablesUsed
[] = 'change_tag';
13 $this->tablesUsed
[] = 'change_tag_def';
14 $this->tablesUsed
[] = 'tag_summary';
17 // TODO only modifyDisplayQuery and getSoftwareTags are tested, nothing else is
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']
29 if ( isset( $modifiedQuery['exception'] ) ) {
30 $this->setExpectedException( $modifiedQuery['exception'] );
32 ChangeTags
::modifyDisplayQuery(
36 $origQuery['join_conds'],
37 $origQuery['options'],
40 if ( !isset( $modifiedQuery['exception'] ) ) {
41 $this->assertArrayEquals(
44 /* ordered = */ false,
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
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' ],
61 'simple recentchanges query' => [
63 'tables' => [ 'recentchanges' ],
64 'fields' => [ 'rc_id', 'rc_timestamp' ],
65 'conds' => [ "rc_timestamp > '20170714183203'" ],
67 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
70 true, // tag filtering enabled
72 'tables' => [ 'recentchanges' ],
73 'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
74 'conds' => [ "rc_timestamp > '20170714183203'" ],
76 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
79 'simple query with strings' => [
81 'tables' => 'recentchanges',
83 'conds' => "rc_timestamp > '20170714183203'",
85 'options' => 'ORDER BY rc_timestamp DESC',
88 true, // tag filtering enabled
90 'tables' => [ 'recentchanges' ],
91 'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
92 'conds' => [ "rc_timestamp > '20170714183203'" ],
94 'options' => [ 'ORDER BY rc_timestamp DESC' ],
97 'recentchanges query with single tag filter' => [
99 'tables' => [ 'recentchanges' ],
100 'fields' => [ 'rc_id', 'rc_timestamp' ],
101 'conds' => [ "rc_timestamp > '20170714183203'" ],
103 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
106 true, // tag filtering enabled
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' ],
115 'logging query with single tag filter and strings' => [
117 'tables' => 'logging',
118 'fields' => 'log_id',
119 'conds' => "log_timestamp > '20170714183203'",
121 'options' => 'ORDER BY log_timestamp DESC',
124 true, // tag filtering enabled
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' ],
133 'revision query with single tag filter' => [
135 'tables' => [ 'revision' ],
136 'fields' => [ 'rev_id', 'rev_timestamp' ],
137 'conds' => [ "rev_timestamp > '20170714183203'" ],
139 'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
142 true, // tag filtering enabled
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' ],
151 'archive query with single tag filter' => [
153 'tables' => [ 'archive' ],
154 'fields' => [ 'ar_id', 'ar_timestamp' ],
155 'conds' => [ "ar_timestamp > '20170714183203'" ],
157 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
160 true, // tag filtering enabled
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' ],
169 'unsupported table name throws exception (even without tag filter)' => [
171 'tables' => [ 'foobar' ],
172 'fields' => [ 'fb_id', 'fb_timestamp' ],
173 'conds' => [ "fb_timestamp > '20170714183203'" ],
175 'options' => [ 'ORDER BY' => 'fb_timestamp DESC' ],
178 true, // tag filtering enabled
179 [ 'exception' => MWException
::class ]
181 'tag filter ignored when tag filtering is disabled' => [
183 'tables' => [ 'archive' ],
184 'fields' => [ 'ar_id', 'ar_timestamp' ],
185 'conds' => [ "ar_timestamp > '20170714183203'" ],
187 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
190 false, // tag filtering disabled
192 'tables' => [ 'archive' ],
193 'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
194 'conds' => [ "ar_timestamp > '20170714183203'" ],
196 'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
199 'recentchanges query with multiple tag filter' => [
201 'tables' => [ 'recentchanges' ],
202 'fields' => [ 'rc_id', 'rc_timestamp' ],
203 'conds' => [ "rc_timestamp > '20170714183203'" ],
205 'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
208 true, // tag filtering enabled
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' ],
217 'recentchanges query with multiple tag filter that already has DISTINCT' => [
219 'tables' => [ 'recentchanges' ],
220 'fields' => [ 'rc_id', 'rc_timestamp' ],
221 'conds' => [ "rc_timestamp > '20170714183203'" ],
223 'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
226 true, // tag filtering enabled
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' ],
235 'recentchanges query with multiple tag filter with strings' => [
237 'tables' => 'recentchanges',
239 'conds' => "rc_timestamp > '20170714183203'",
241 'options' => 'ORDER BY rc_timestamp DESC',
244 true, // tag filtering enabled
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' ],
256 public static function dataGetSoftwareTags() {
260 'mw-contentModelChange' => true,
261 'mw-redirect' => true,
262 'mw-rollback' => true,
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
284 'mw-changed-redirect-target'
306 * @dataProvider dataGetSoftwareTags
307 * @covers ChangeTags::getSoftwareTags
309 public function testGetSoftwareTags( $softwareTags, $expected ) {
310 $this->setMwGlobals( 'wgSoftwareTags', $softwareTags );
312 $actual = ChangeTags
::getSoftwareTags();
313 // Order of tags in arrays is not important
316 $this->assertEquals( $expected, $actual );
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', '*' );
326 ChangeTags
::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
328 $dbr = wfGetDB( DB_REPLICA
);
330 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
331 $this->assertEquals( [], iterator_to_array( $res, false ) );
345 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
346 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
349 ChangeTags
::updateTags( [ 'tag1', 'tag3' ], [], $rcId );
351 $dbr = wfGetDB( DB_REPLICA
);
353 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
354 $this->assertEquals( [], iterator_to_array( $res, false ) );
378 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
379 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
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', '*' );
389 ChangeTags
::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
391 $dbr = wfGetDB( DB_REPLICA
);
395 'ctd_name' => 'tag1',
400 'ctd_name' => 'tag2',
405 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
406 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
420 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
421 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
424 ChangeTags
::updateTags( [ 'tag1', 'tag3' ], [], $rcId );
426 $dbr = wfGetDB( DB_REPLICA
);
430 'ctd_name' => 'tag1',
435 'ctd_name' => 'tag2',
440 'ctd_name' => 'tag3',
445 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
446 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
470 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
471 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
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', '*' );
481 ChangeTags
::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
483 ChangeTags
::updateTags( [], [ 'tag2' ], $rcId );
485 $dbr = wfGetDB( DB_REPLICA
);
487 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
488 $this->assertEquals( [], iterator_to_array( $res, false ) );
497 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
498 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );
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', '*' );
508 ChangeTags
::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
510 ChangeTags
::updateTags( [], [ 'tag2' ], $rcId );
512 $dbr = wfGetDB( DB_REPLICA
);
516 'ctd_name' => 'tag1',
521 $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_id', 'ctd_count' ], '' );
522 $this->assertEquals( $expected, iterator_to_array( $res, false ) );
531 $res2 = $dbr->select( 'change_tag', [ 'ct_tag', 'ct_tag_id', 'ct_rc_id' ], '' );
532 $this->assertEquals( $expected2, iterator_to_array( $res2, false ) );