1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Wikibase\Client\Tests\ChangeModification; |
4
|
|
|
|
5
|
|
|
use Language; |
6
|
|
|
use MediaWiki\MediaWikiServices; |
7
|
|
|
use MediaWikiIntegrationTestCase; |
8
|
|
|
use RecentChange; |
9
|
|
|
use Title; |
10
|
|
|
use Wikibase\Client\ChangeModification\ChangeVisibilityNotificationJob; |
11
|
|
|
use Wikibase\Client\RecentChanges\RecentChangeFactory; |
12
|
|
|
use Wikibase\Client\RecentChanges\SiteLinkCommentCreator; |
13
|
|
|
use Wikibase\DataModel\Entity\EntityIdParser; |
14
|
|
|
use Wikibase\DataModel\Entity\PropertyId; |
15
|
|
|
use Wikibase\DataModel\Services\Diff\EntityDiffer; |
16
|
|
|
use Wikibase\Lib\Changes\EntityChangeFactory; |
17
|
|
|
use Wikibase\Lib\Changes\RepoRevisionIdentifier; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @covers \Wikibase\Client\ChangeModification\ChangeVisibilityNotificationJob |
21
|
|
|
* |
22
|
|
|
* @group Wikibase |
23
|
|
|
* @group WikibaseChange |
24
|
|
|
* |
25
|
|
|
* @license GPL-2.0-or-later |
26
|
|
|
* @author Marius Hoch |
27
|
|
|
*/ |
28
|
|
|
class ChangeVisibilityNotificationJobTest extends MediaWikiIntegrationTestCase { |
29
|
|
|
|
30
|
|
|
protected function setUp(): void { |
31
|
|
|
parent::setUp(); |
32
|
|
|
|
33
|
|
|
$this->tablesUsed[] = 'recentchanges'; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
public function visibilityNotificationProvider() { |
37
|
|
|
return [ |
38
|
|
|
'redact nothing' => [ |
39
|
|
|
[], |
40
|
|
|
[ |
41
|
|
|
new RepoRevisionIdentifier( 'Q404', '20111111111111', 1002, 1001 ), |
|
|
|
|
42
|
|
|
], |
43
|
|
|
6, |
44
|
|
|
], |
45
|
|
|
'redact one entry with one RepoRevisionIdentifier' => [ |
46
|
|
|
[ 'UNIQ-001' ], |
47
|
|
|
[ |
48
|
|
|
new RepoRevisionIdentifier( 'Q42', '20111111111111', 1002, 1001 ), |
|
|
|
|
49
|
|
|
], |
50
|
|
|
6, |
51
|
|
|
], |
52
|
|
|
'redact one entry with two RepoRevisionIdentifiers' => [ |
53
|
|
|
[ 'UNIQ-001' ], |
54
|
|
|
[ |
55
|
|
|
new RepoRevisionIdentifier( 'Q42', '20111111111111', 1002, 1001 ), |
|
|
|
|
56
|
|
|
new RepoRevisionIdentifier( 'Q45345', '20111111111111', 1002, 1001 ), |
|
|
|
|
57
|
|
|
], |
58
|
|
|
6, |
59
|
|
|
], |
60
|
|
|
'redact two entries with one RepoRevisionIdentifier' => [ |
61
|
|
|
[ 'UNIQ-005', 'UNIQ-006' ], |
62
|
|
|
[ |
63
|
|
|
new RepoRevisionIdentifier( 'Q2013', '20111111111115', 2013, 2014 ), |
|
|
|
|
64
|
|
|
], |
65
|
|
|
12, |
66
|
|
|
], |
67
|
|
|
'redact multiple entries with multiple RepoRevisionIdentifiers' => [ |
68
|
|
|
[ 'UNIQ-001', 'UNIQ-005', 'UNIQ-006' ], |
69
|
|
|
[ |
70
|
|
|
new RepoRevisionIdentifier( 'Q42', '20111111111111', 1002, 1001 ), |
|
|
|
|
71
|
|
|
new RepoRevisionIdentifier( 'Q45345', '20111111111111', 1002, 1001 ), |
|
|
|
|
72
|
|
|
new RepoRevisionIdentifier( 'Q2013', '20111111111115', 2013, 2014 ), |
|
|
|
|
73
|
|
|
], |
74
|
|
|
4, |
75
|
|
|
], |
76
|
|
|
]; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @dataProvider visibilityNotificationProvider |
81
|
|
|
*/ |
82
|
|
|
public function testRun( array $expectedRedactedTitles, array $revisionIdentifiers, $visibilityBitFlag ) { |
83
|
|
|
$this->initRecentChanges(); |
84
|
|
|
|
85
|
|
|
$job = new ChangeVisibilityNotificationJob( |
86
|
|
|
MediaWikiServices::getInstance()->getDBLoadBalancerFactory(), |
87
|
|
|
MediaWikiServices::getInstance()->getMainConfig()->get( 'UpdateRowsPerQuery' ), |
88
|
|
|
[ |
89
|
|
|
'revisionIdentifiersJson' => $this->revisionIdentifiersToJson( $revisionIdentifiers ), |
90
|
|
|
'visibilityBitFlag' => $visibilityBitFlag |
91
|
|
|
] |
92
|
|
|
); |
93
|
|
|
$job->run(); |
94
|
|
|
|
95
|
|
|
$this->assertTitlesRedacted( $expectedRedactedTitles, $visibilityBitFlag ); |
96
|
|
|
$this->assertOtherTitlesUntouched( $expectedRedactedTitles ); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* JSON encode the given RepoRevisionIdentifiers |
101
|
|
|
* |
102
|
|
|
* @param RepoRevisionIdentifier[] $revisionIdentifiers |
103
|
|
|
* |
104
|
|
|
* @return string JSON |
105
|
|
|
*/ |
106
|
|
|
private function revisionIdentifiersToJson( array $revisionIdentifiers ): string { |
107
|
|
|
return json_encode( |
108
|
|
|
array_map( |
109
|
|
|
function ( RepoRevisionIdentifier $revisionIdentifier ) { |
110
|
|
|
return $revisionIdentifier->toArray(); |
111
|
|
|
}, |
112
|
|
|
$revisionIdentifiers |
113
|
|
|
) |
114
|
|
|
); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
public function testRun_recentChangeFactoryRoundtrip() { |
118
|
|
|
$this->initRecentChanges(); |
119
|
|
|
|
120
|
|
|
$recentChangeFactory = new RecentChangeFactory( |
121
|
|
|
Language::factory( 'qqx' ), |
122
|
|
|
$this->createMock( SiteLinkCommentCreator::class ) |
123
|
|
|
); |
124
|
|
|
$entityChangeFactory = new EntityChangeFactory( |
125
|
|
|
$this->createMock( EntityDiffer::class ), |
126
|
|
|
$this->createMock( EntityIdParser::class ), |
127
|
|
|
[] |
128
|
|
|
); |
129
|
|
|
|
130
|
|
|
$entityChange = $entityChangeFactory->newForEntity( |
131
|
|
|
'blah', |
132
|
|
|
new PropertyId( 'P42' ), |
133
|
|
|
[ 'user_text' => 'a-nice-user' ] |
134
|
|
|
); |
135
|
|
|
$entityChange->setTimestamp( '20161111111111' ); |
136
|
|
|
$entityChange->setMetadata( [ |
137
|
|
|
'rev_id' => 342, |
138
|
|
|
'parent_id' => 343 |
139
|
|
|
] ); |
140
|
|
|
$additionalRecentChange = $recentChangeFactory->newRecentChange( |
141
|
|
|
$entityChange, |
142
|
|
|
Title::newFromText( 'UNIQ-FROM-RecentChangeFactory' ) |
143
|
|
|
); |
144
|
|
|
$additionalRecentChange->save(); |
145
|
|
|
|
146
|
|
|
$job = new ChangeVisibilityNotificationJob( |
147
|
|
|
MediaWikiServices::getInstance()->getDBLoadBalancerFactory(), |
148
|
|
|
MediaWikiServices::getInstance()->getMainConfig()->get( 'UpdateRowsPerQuery' ), |
149
|
|
|
[ |
150
|
|
|
'revisionIdentifiersJson' => $this->revisionIdentifiersToJson( [ |
151
|
|
|
new RepoRevisionIdentifier( 'P42', '20161111111111', 342, 343 ), |
|
|
|
|
152
|
|
|
] ), |
153
|
|
|
'visibilityBitFlag' => 16 |
154
|
|
|
] |
155
|
|
|
); |
156
|
|
|
$job->run(); |
157
|
|
|
|
158
|
|
|
$this->assertTitlesRedacted( [ 'UNIQ-FROM-RecentChangeFactory' ], 16 ); |
159
|
|
|
$this->assertOtherTitlesUntouched( [ 'UNIQ-FROM-RecentChangeFactory' ] ); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Make sure rows for titles that are in $expectedRedactedTitles correctly got changed. |
164
|
|
|
*/ |
165
|
|
|
private function assertTitlesRedacted( array $expectedRedactedTitles, $visibilityBitFlag ) { |
166
|
|
|
if ( !$expectedRedactedTitles ) { |
|
|
|
|
167
|
|
|
$this->assertTrue( (bool)"Nothing to do" ); |
168
|
|
|
return; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
$dbr = wfGetDB( DB_REPLICA ); |
172
|
|
|
|
173
|
|
|
$rcRedactedCount = $dbr->selectRowCount( |
174
|
|
|
'recentchanges', |
175
|
|
|
'rc_deleted', |
176
|
|
|
[ |
177
|
|
|
'rc_title' => $expectedRedactedTitles, |
178
|
|
|
'rc_deleted' => $visibilityBitFlag |
179
|
|
|
], |
180
|
|
|
__METHOD__ |
181
|
|
|
); |
182
|
|
|
$this->assertSame( count( $expectedRedactedTitles ), $rcRedactedCount, 'Missing rc_deleted changes.' ); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Make sure rows for titles that are not in $expectedRedactedTitles didn't get changed. |
187
|
|
|
*/ |
188
|
|
|
private function assertOtherTitlesUntouched( array $expectedRedactedTitles ) { |
189
|
|
|
$dbr = wfGetDB( DB_REPLICA ); |
190
|
|
|
|
191
|
|
|
// Count the rows that have rc_deleted set that are not in $expectedRedactedTitles |
192
|
|
|
$where = [ 'rc_deleted > 0' ]; |
193
|
|
|
if ( $expectedRedactedTitles ) { |
|
|
|
|
194
|
|
|
$where[] = 'rc_title NOT IN (' . $dbr->makeList( $expectedRedactedTitles ) . ')'; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
$rcFalsePositiveCount = $dbr->selectRowCount( |
198
|
|
|
'recentchanges', |
199
|
|
|
'rc_deleted', |
200
|
|
|
$where, |
201
|
|
|
__METHOD__ |
202
|
|
|
); |
203
|
|
|
|
204
|
|
|
$this->assertSame( 0, $rcFalsePositiveCount, 'Unexpected rc_deleted changes.' ); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
public function testToString() { |
208
|
|
|
$job = new ChangeVisibilityNotificationJob( |
209
|
|
|
MediaWikiServices::getInstance()->getDBLoadBalancerFactory(), |
210
|
|
|
MediaWikiServices::getInstance()->getMainConfig()->get( 'UpdateRowsPerQuery' ), |
211
|
|
|
[ |
212
|
|
|
'revisionIdentifiersJson' => $this->revisionIdentifiersToJson( [ |
213
|
|
|
new RepoRevisionIdentifier( 'Q1', '1', 1, 1 ), |
|
|
|
|
214
|
|
|
] ), |
215
|
|
|
'visibilityBitFlag' => 6 |
216
|
|
|
] |
217
|
|
|
); |
218
|
|
|
|
219
|
|
|
$this->assertRegExp( '/^ChangeVisibilityNotification/', $job->toString() ); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
private function newChange( array $changeData ) { |
223
|
|
|
if ( isset( $changeData['rc_params'] ) && !is_string( $changeData['rc_params'] ) ) { |
224
|
|
|
$changeData['rc_params'] = serialize( $changeData['rc_params'] ); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
$defaults = [ |
228
|
|
|
'rc_id' => 0, |
229
|
|
|
'rc_timestamp' => '20000000000000', |
230
|
|
|
'rc_user' => 0, |
231
|
|
|
'rc_user_text' => '', |
232
|
|
|
'rc_namespace' => 0, |
233
|
|
|
'rc_title' => '', |
234
|
|
|
'rc_comment' => '', |
235
|
|
|
'rc_minor' => false, |
236
|
|
|
'rc_bot' => false, |
237
|
|
|
'rc_new' => false, |
238
|
|
|
'rc_cur_id' => 0, |
239
|
|
|
'rc_this_oldid' => 0, |
240
|
|
|
'rc_last_oldid' => 0, |
241
|
|
|
'rc_type' => RC_EXTERNAL, |
242
|
|
|
'rc_source' => RecentChangeFactory::SRC_WIKIBASE, |
243
|
|
|
'rc_patrolled' => 0, |
244
|
|
|
'rc_ip' => '127.0.0.1', |
245
|
|
|
'rc_old_len' => 0, |
246
|
|
|
'rc_new_len' => 0, |
247
|
|
|
'rc_deleted' => false, |
248
|
|
|
'rc_logid' => 0, |
249
|
|
|
'rc_log_type' => null, |
250
|
|
|
'rc_log_action' => '', |
251
|
|
|
'rc_params' => '', |
252
|
|
|
]; |
253
|
|
|
|
254
|
|
|
$changeData = array_merge( $defaults, $changeData ); |
255
|
|
|
|
256
|
|
|
// The faked-up RecentChange row needs to have the proper fields for |
257
|
|
|
// MediaWiki core change Ic3a434c0. And can't have them without that |
258
|
|
|
// patch or the ->save() in initRecentChanges() will fail. |
259
|
|
|
$changeData += [ |
260
|
|
|
'rc_comment_text' => $changeData['rc_comment'], |
261
|
|
|
'rc_comment_data' => null, |
262
|
|
|
]; |
263
|
|
|
|
264
|
|
|
$change = RecentChange::newFromRow( (object)$changeData ); |
265
|
|
|
$change->setExtra( [ |
266
|
|
|
'pageStatus' => 'changed' |
267
|
|
|
] ); |
268
|
|
|
|
269
|
|
|
return $change; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Empty the recentchanges table and put some changes in there. |
274
|
|
|
* |
275
|
|
|
* All changes have a unique rc_title value to make them easy to identify. |
276
|
|
|
*/ |
277
|
|
|
private function initRecentChanges() { |
278
|
|
|
wfGetDB( DB_MASTER )->delete( 'recentchanges', '*' ); |
279
|
|
|
|
280
|
|
|
$change = $this->newChange( [ |
281
|
|
|
'rc_timestamp' => '20111111111111', |
282
|
|
|
'rc_user' => 23, |
283
|
|
|
'rc_user_text' => 'Test', |
284
|
|
|
'rc_namespace' => 0, |
285
|
|
|
'rc_title' => 'UNIQ-001', |
286
|
|
|
'rc_comment' => 'Testing', |
287
|
|
|
'rc_type' => RC_EXTERNAL, |
288
|
|
|
'rc_source' => RecentChangeFactory::SRC_WIKIBASE, |
289
|
|
|
'rc_last_oldid' => 11, |
290
|
|
|
'rc_this_oldid' => 12, |
291
|
|
|
'rc_params' => [ |
292
|
|
|
'wikibase-repo-change' => [ |
293
|
|
|
'parent_id' => 1001, |
294
|
|
|
'rev_id' => 1002, |
295
|
|
|
'object_id' => 'Q42' |
296
|
|
|
] |
297
|
|
|
] |
298
|
|
|
] ); |
299
|
|
|
$change->save(); |
300
|
|
|
|
301
|
|
|
$change = $this->newChange( [ |
302
|
|
|
'rc_timestamp' => '20111111111111', |
303
|
|
|
'rc_user' => 23, |
304
|
|
|
'rc_user_text' => 'Test', |
305
|
|
|
'rc_namespace' => 0, |
306
|
|
|
'rc_title' => 'UNIQ-002', |
307
|
|
|
'rc_comment' => 'Testing', |
308
|
|
|
'rc_type' => RC_EXTERNAL, |
309
|
|
|
'rc_source' => RecentChangeFactory::SRC_WIKIBASE, |
310
|
|
|
'rc_last_oldid' => 11, |
311
|
|
|
'rc_this_oldid' => 12, |
312
|
|
|
'rc_params' => [ |
313
|
|
|
'wikibase-repo-change' => [ |
314
|
|
|
'parent_id' => 1001, |
315
|
|
|
'rev_id' => 1002, |
316
|
|
|
'object_id' => 'Q24' |
317
|
|
|
] |
318
|
|
|
] |
319
|
|
|
] ); |
320
|
|
|
$change->save(); |
321
|
|
|
|
322
|
|
|
$change = $this->newChange( [ |
323
|
|
|
'rc_timestamp' => '20121212121212', |
324
|
|
|
'rc_user' => 23, |
325
|
|
|
'rc_user_text' => 'Test', |
326
|
|
|
'rc_namespace' => 0, |
327
|
|
|
'rc_title' => 'UNIQ-003', |
328
|
|
|
'rc_comment' => 'Testing', |
329
|
|
|
'rc_type' => RC_EXTERNAL, |
330
|
|
|
'rc_source' => 'foo', // different source, will always be ignored |
331
|
|
|
'rc_last_oldid' => 11, |
332
|
|
|
'rc_this_oldid' => 12, |
333
|
|
|
'rc_params' => [ |
334
|
|
|
'wikibase-repo-change' => [ |
335
|
|
|
'parent_id' => 1001, |
336
|
|
|
'rev_id' => 1002, |
337
|
|
|
'object_id' => 'Q42' |
338
|
|
|
] |
339
|
|
|
] |
340
|
|
|
] ); |
341
|
|
|
$change->save(); |
342
|
|
|
|
343
|
|
|
$change = $this->newChange( [ |
344
|
|
|
'rc_timestamp' => '20111111111111', |
345
|
|
|
'rc_user' => 23, |
346
|
|
|
'rc_user_text' => 'Test', |
347
|
|
|
'rc_namespace' => 0, |
348
|
|
|
'rc_title' => 'UNIQ-004', |
349
|
|
|
'rc_comment' => 'Testing', |
350
|
|
|
'rc_type' => RC_EXTERNAL, |
351
|
|
|
'rc_source' => RecentChangeFactory::SRC_WIKIBASE, |
352
|
|
|
'rc_last_oldid' => 11, |
353
|
|
|
'rc_this_oldid' => 12, |
354
|
|
|
'rc_params' => [ |
355
|
|
|
'wikibase-repo-change' => [ |
356
|
|
|
'parent_id' => 1, |
357
|
|
|
'rev_id' => 2, |
358
|
|
|
'object_id' => 'Q42' |
359
|
|
|
] |
360
|
|
|
] |
361
|
|
|
] ); |
362
|
|
|
$change->save(); |
363
|
|
|
|
364
|
|
|
$change = $this->newChange( [ |
365
|
|
|
'rc_timestamp' => '20111111111115', |
366
|
|
|
'rc_user' => 23, |
367
|
|
|
'rc_user_text' => 'Test', |
368
|
|
|
'rc_namespace' => 0, |
369
|
|
|
'rc_title' => 'UNIQ-005', |
370
|
|
|
'rc_comment' => 'Testing', |
371
|
|
|
'rc_type' => RC_EXTERNAL, |
372
|
|
|
'rc_source' => RecentChangeFactory::SRC_WIKIBASE, |
373
|
|
|
'rc_last_oldid' => 11, |
374
|
|
|
'rc_this_oldid' => 12, |
375
|
|
|
'rc_params' => [ |
376
|
|
|
'wikibase-repo-change' => [ |
377
|
|
|
'parent_id' => 2014, |
378
|
|
|
'rev_id' => 2013, |
379
|
|
|
'object_id' => 'Q2013' |
380
|
|
|
] |
381
|
|
|
] |
382
|
|
|
] ); |
383
|
|
|
$change->save(); |
384
|
|
|
|
385
|
|
|
$change = $this->newChange( [ |
386
|
|
|
'rc_timestamp' => '20111111111115', |
387
|
|
|
'rc_user' => 23, |
388
|
|
|
'rc_user_text' => 'Test', |
389
|
|
|
'rc_namespace' => 0, |
390
|
|
|
'rc_title' => 'UNIQ-006', |
391
|
|
|
'rc_comment' => 'Testing', |
392
|
|
|
'rc_type' => RC_EXTERNAL, |
393
|
|
|
'rc_source' => RecentChangeFactory::SRC_WIKIBASE, |
394
|
|
|
'rc_last_oldid' => 11, |
395
|
|
|
'rc_this_oldid' => 12, |
396
|
|
|
'rc_params' => [ |
397
|
|
|
'wikibase-repo-change' => [ |
398
|
|
|
'parent_id' => 2014, |
399
|
|
|
'rev_id' => 2013, |
400
|
|
|
'object_id' => 'Q2013' |
401
|
|
|
] |
402
|
|
|
] |
403
|
|
|
] ); |
404
|
|
|
$change->save(); |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
} |
408
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.