Improve coverage for several classes

Change-Id: I257524dd22a5617ac47a3565a5b8fe4855aa67c7
This commit is contained in:
Daimona Eaytoy 2021-01-16 15:52:09 +01:00
parent 717abf4300
commit 5fcc5ef357
7 changed files with 427 additions and 115 deletions

View file

@ -31,6 +31,7 @@ class AbuseFilterHooks {
/**
* Called right after configuration has been loaded.
* @codeCoverageIgnore Mainly deprecation warnings and other things that can be tested by running the updater
*/
public static function onRegistration() {
global $wgAuthManagerAutoConfig, $wgActionFilteredLogs, $wgAbuseFilterProfile,

View file

@ -85,7 +85,12 @@ class BlockAutopromoteStore {
'5::newgroups' => []
] );
$logEntry->setComment( $msg );
$logEntry->publish( $logEntry->insert() );
if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
// FIXME Remove this check once ManualLogEntry is servicified (T253717)
// @codeCoverageIgnoreStart
$logEntry->publish( $logEntry->insert() );
// @codeCoverageIgnoreEnd
}
return true;
}
@ -118,7 +123,12 @@ class BlockAutopromoteStore {
'5::newgroups' => []
] );
$logEntry->setPerformer( $performer );
$logEntry->publish( $logEntry->insert() );
if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
// FIXME Remove this check once ManualLogEntry is servicified (T253717)
// @codeCoverageIgnoreStart
$logEntry->publish( $logEntry->insert() );
// @codeCoverageIgnoreEnd
}
return true;
}

View file

@ -1,113 +0,0 @@
<?php
use MediaWiki\Extension\AbuseFilter\AbuseFilterServices;
use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder;
/**
* @group Test
* @group AbuseFilter
* @group Database
* @coversDefaultClass \MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore
*/
class VariablesBlobStoreTest extends MediaWikiIntegrationTestCase {
/** @inheritDoc */
protected $tablesUsed = [
'text'
];
/**
* Test storing and loading the var dump. See also AbuseFilterConsequencesTest::testVarDump
*
* @param array $variables Map of [ name => value ] to build a variable holder with
* @param array|null $expectedValues Null to use $variables
* @covers ::loadVarDump
* @covers ::storeVarDump
* @dataProvider provideVariables
*/
public function testVarDump( array $variables, array $expectedValues = null ) {
$varBlobStore = AbuseFilterServices::getVariablesBlobStore();
$holder = VariableHolder::newFromArray( $variables );
$insertID = $varBlobStore->storeVarDump( $holder );
$dump = $varBlobStore->loadVarDump( $insertID );
$expected = $expectedValues ? VariableHolder::newFromArray( $expectedValues ) : $holder;
$this->assertEquals( $expected, $dump, 'The var dump is not saved correctly' );
}
/**
* Data provider for testVarDump
*
* @return array
*/
public function provideVariables() {
return [
'Only basic variables' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text'
]
],
'Normal case' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'user_editcount' => 15,
'added_lines' => [ 'Foo', '', 'Bar' ]
]
],
'Deprecated variables' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'article_articleid' => 11745,
'article_first_contributor' => 'Good guy'
],
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'page_id' => 11745,
'page_first_contributor' => 'Good guy'
]
],
'Move action' => [
[
'action' => 'move',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'all_links' => [ 'https://en.wikipedia.org' ],
'moved_to_id' => 156,
'moved_to_prefixedtitle' => 'MediaWiki:Foobar.js',
'new_content_model' => CONTENT_MODEL_JAVASCRIPT
]
],
'Delete action' => [
[
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'timestamp' => 1546000295,
'action' => 'delete',
'page_namespace' => 114
]
],
'Disabled vars' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'old_html' => 'Foo <small>bar</small> <s>lol</s>.',
'old_text' => 'Foobar'
]
],
'Account creation' => [
[
'action' => 'createaccount',
'accountname' => 'XXX'
]
]
];
}
}

View file

@ -0,0 +1,81 @@
<?php
namespace MediaWiki\Extension\AbuseFilter\Tests\Unit;
use BagOStuff;
use Generator;
use HashBagOStuff;
use MediaWiki\Extension\AbuseFilter\BlockAutopromoteStore;
use MediaWiki\Extension\AbuseFilter\FilterUser;
use MediaWiki\User\UserIdentityValue;
use MediaWikiUnitTestCase;
use Psr\Log\NullLogger;
/**
* @coversDefaultClass \MediaWiki\Extension\AbuseFilter\BlockAutopromoteStore
* @covers ::__construct
*/
class BlockAutopromoteStoreTest extends MediaWikiUnitTestCase {
private function getStore( BagOStuff $cache ) : BlockAutopromoteStore {
return new BlockAutopromoteStore(
$cache,
new NullLogger(),
$this->createMock( FilterUser::class )
);
}
public function provideBlockAutopromote() : Generator {
$cache = $this->createMock( BagOStuff::class );
$cache->expects( $this->once() )->method( 'set' )->willReturn( false );
$cache->method( 'makeKey' )->willReturn( 'foo' );
yield 'cannot set' => [ $cache, false ];
yield 'success' => [ new HashBagOStuff(), true ];
}
/**
* @covers ::blockAutoPromote
* @dataProvider provideBlockAutopromote
*/
public function testBlockAutopromote( BagOStuff $cache, bool $expected ) {
$store = $this->getStore( $cache );
$target = new UserIdentityValue( 1, 'Blocked user', 43 );
$this->assertSame( $expected, $store->blockAutoPromote( $target, '', 1 ) );
}
public function provideUnblockAutopromote() : Generator {
yield 'not blocked' => [ new HashBagOStuff(), false ];
$cache = $this->createMock( BagOStuff::class );
$cache->expects( $this->once() )->method( 'changeTTL' )->willReturn( true );
$cache->method( 'makeKey' )->willReturn( 'foo' );
yield 'success' => [ $cache, true ];
}
/**
* @covers ::unblockAutoPromote
* @dataProvider provideUnblockAutopromote
*/
public function testUnblockAutopromote( BagOStuff $cache, bool $expected ) {
$store = $this->getStore( $cache );
$target = new UserIdentityValue( 1, 'Blocked user', 43 );
$this->assertSame( $expected, $store->unblockAutoPromote( $target, $target, '' ) );
}
/**
* @covers ::blockAutoPromote
* @covers ::getAutoPromoteBlockStatus
* @covers ::unblockAutopromote
* @covers ::getAutoPromoteBlockKey
*/
public function testRoundTrip() {
$cache = new HashBagOStuff();
$store = $this->getStore( $cache );
$target = new UserIdentityValue( 1, 'Blocked user', 43 );
$this->assertTrue( $store->blockAutoPromote( $target, '', 3000 ), 'block' );
$this->assertSame( 1, $store->getAutoPromoteBlockStatus( $target ), 'should be blocked' );
$this->assertTrue( $store->unblockAutoPromote( $target, $target, '' ), 'can unblock' );
$this->assertSame( 0, $store->getAutoPromoteBlockStatus( $target ), 'should not be blocked' );
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace MediaWiki\Extension\AbuseFilter\Tests\Unit\VariableGenerator;
use MediaWiki\Extension\AbuseFilter\Hooks\AbuseFilterHookRunner;
use MediaWiki\Extension\AbuseFilter\TextExtractor;
use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory;
use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder;
use MediaWikiUnitTestCase;
use MimeAnalyzer;
use RecentChange;
use RepoGroup;
use Title;
use User;
/**
* @group Test
* @group AbuseFilter
*
* @coversDefaultClass \MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory
* @covers ::__construct
*/
class VariableGeneratorFactoryTest extends MediaWikiUnitTestCase {
private function getFactory() : VariableGeneratorFactory {
return new VariableGeneratorFactory(
$this->createMock( AbuseFilterHookRunner::class ),
$this->createMock( TextExtractor::class ),
$this->createMock( MimeAnalyzer::class ),
$this->createMock( RepoGroup::class )
);
}
/**
* @covers ::newGenerator
*/
public function testNewGenerator() {
$this->getFactory()->newGenerator( new VariableHolder() );
$this->addToAssertionCount( 1 );
}
/**
* @covers ::newRunGenerator
*/
public function testNewRunGenerator() {
$this->getFactory()->newRunGenerator(
$this->createMock( User::class ),
$this->createMock( Title::class ),
new VariableHolder()
);
$this->addToAssertionCount( 1 );
}
/**
* @covers ::newRCGenerator
*/
public function testNewRCGenerator() {
$this->getFactory()->newRCGenerator(
$this->createMock( RecentChange::class ),
$this->createMock( User::class ),
new VariableHolder()
);
$this->addToAssertionCount( 1 );
}
}

View file

@ -0,0 +1,216 @@
<?php
namespace MediaWiki\Extension\AbuseFilter\Tests\Unit\Variables;
use FormatJson;
use MediaWiki\Extension\AbuseFilter\KeywordsManager;
use MediaWiki\Extension\AbuseFilter\Parser\AFPData;
use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder;
use MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore;
use MediaWiki\Extension\AbuseFilter\Variables\VariablesManager;
use MediaWiki\Storage\BlobAccessException;
use MediaWiki\Storage\BlobStore;
use MediaWiki\Storage\BlobStoreFactory;
use MediaWikiUnitTestCase;
use Wikimedia\TestingAccessWrapper;
/**
* @group Test
* @group AbuseFilter
* @coversDefaultClass \MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore
* @covers ::__construct
*/
class VariablesBlobStoreTest extends MediaWikiUnitTestCase {
private function getStore(
BlobStoreFactory $blobStoreFactory = null,
BlobStore $blobStore = null
) : VariablesBlobStore {
$manager = $this->createMock( VariablesManager::class );
$manager->method( 'dumpAllVars' )->willReturnCallback( function ( VariableHolder $holder ) {
$ret = [];
foreach ( $holder->getVars() as $name => $var ) {
if ( $var instanceof AFPData ) {
$ret[$name] = $var->toNative();
}
}
return $ret;
} );
$manager->method( 'translateDeprecatedVars' )->willReturnCallback( function ( VariableHolder $holder ) {
$depVars = TestingAccessWrapper::constant( KeywordsManager::class, 'DEPRECATED_VARS' );
foreach ( $holder->getVars() as $name => $value ) {
if ( array_key_exists( $name, $depVars ) ) {
$holder->setVar( $depVars[$name], $value );
$holder->removeVar( $name );
}
}
} );
return new VariablesBlobStore(
$manager,
$blobStoreFactory ?? $this->createMock( BlobStoreFactory::class ),
$blobStore ?? $this->createMock( BlobStore::class ),
null
);
}
/**
* @covers ::storeVarDump
*/
public function testStoreVarDump() {
$expectID = 123456;
$blobStore = $this->createMock( BlobStore::class );
$blobStore->expects( $this->once() )->method( 'storeBlob' )->willReturn( $expectID );
$blobStoreFactory = $this->createMock( BlobStoreFactory::class );
$blobStoreFactory->method( 'newBlobStore' )->willReturn( $blobStore );
$varBlobStore = $this->getStore( $blobStoreFactory );
$this->assertSame( $expectID, $varBlobStore->storeVarDump( new VariableHolder() ) );
}
/**
* @covers ::loadVarDump
*/
public function testLoadVarDump() {
$vars = [ 'foo-variable' => 42 ];
$blob = FormatJson::encode( $vars );
$blobStore = $this->createMock( BlobStore::class );
$blobStore->expects( $this->once() )->method( 'getBlob' )->willReturn( $blob );
$varBlobStore = $this->getStore( null, $blobStore );
$loadedVars = $varBlobStore->loadVarDump( 'foo' )->getVars();
$this->assertArrayHasKey( 'foo-variable', $loadedVars );
$this->assertSame( 42, $loadedVars['foo-variable']->toNative() );
}
/**
* @covers ::loadVarDump
*/
public function testLoadVarDump_fail() {
$blobStore = $this->createMock( BlobStore::class );
$blobStore->expects( $this->once() )->method( 'getBlob' )->willThrowException( new BlobAccessException );
$varBlobStore = $this->getStore( null, $blobStore );
$this->assertCount( 0, $varBlobStore->loadVarDump( 'foo' )->getVars() );
}
private function getBlobStore() : BlobStore {
return new class implements BlobStore {
private $blobs;
private function getKey( string $data ) {
return md5( $data );
}
public function getBlob( $blobAddress, $queryFlags = 0 ) {
return $this->blobs[$blobAddress];
}
public function getBlobBatch( $blobAddresses, $queryFlags = 0 ) {
}
public function storeBlob( $data, $hints = [] ) {
$key = $this->getKey( $data );
$this->blobs[$key] = $data;
return $key;
}
public function isReadOnly() {
}
};
}
/**
* @covers ::storeVarDump
* @covers ::loadVarDump
* @dataProvider provideVariables
*/
public function testRoundTrip( array $toStore, array $expected = null ) {
$expected = $expected ?? $toStore;
$blobStore = $this->getBlobStore();
$blobStoreFactory = $this->createMock( BlobStoreFactory::class );
$blobStoreFactory->method( 'newBlobStore' )->willReturn( $blobStore );
$varBlobStore = $this->getStore( $blobStoreFactory, $blobStore );
$storeID = $varBlobStore->storeVarDump( VariableHolder::newFromArray( $toStore ) );
$this->assertIsString( $storeID );
$loadedVars = $varBlobStore->loadVarDump( $storeID )->getVars();
$nativeLoadedVars = array_map( function ( AFPData $el ) {
return $el->toNative();
}, $loadedVars );
$this->assertSame( $expected, $nativeLoadedVars );
}
/**
* Data provider for testVarDump
*
* @return array
*/
public function provideVariables() {
return [
'Only basic variables' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text'
]
],
'Normal case' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'user_editcount' => 15,
'added_lines' => [ 'Foo', '', 'Bar' ]
]
],
'Deprecated variables' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'article_articleid' => 11745,
'article_first_contributor' => 'Good guy'
],
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'page_id' => 11745,
'page_first_contributor' => 'Good guy'
]
],
'Move action' => [
[
'action' => 'move',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'all_links' => [ 'https://en.wikipedia.org' ],
'moved_to_id' => 156,
'moved_to_prefixedtitle' => 'MediaWiki:Foobar.js',
'new_content_model' => CONTENT_MODEL_JAVASCRIPT
]
],
'Delete action' => [
[
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'timestamp' => 1546000295,
'action' => 'delete',
'page_namespace' => 114
]
],
'Disabled vars' => [
[
'action' => 'edit',
'old_wikitext' => 'Old text',
'new_wikitext' => 'New text',
'old_html' => 'Foo <small>bar</small> <s>lol</s>.',
'old_text' => 'Foobar'
]
],
'Account creation' => [
[
'action' => 'createaccount',
'accountname' => 'XXX'
]
]
];
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace MediaWiki\Extension\AbuseFilter\Tests\Unit\Watcher;
use IDatabase;
use MediaWiki\Extension\AbuseFilter\CentralDBManager;
use MediaWiki\Extension\AbuseFilter\Watcher\UpdateHitCountWatcher;
use MediaWikiUnitTestCase;
use Wikimedia\Rdbms\ILoadBalancer;
/**
* @coversDefaultClass \MediaWiki\Extension\AbuseFilter\Watcher\UpdateHitCountWatcher
* @covers ::__construct
*/
class UpdateHitCountWatcherTest extends MediaWikiUnitTestCase {
/**
* @covers ::run
* @covers ::updateHitCounts
*/
public function testRun() {
$localFilters = [ 1, 2, 3 ];
$globalFilters = [ 4, 5, 6 ];
$onTransactionCB = function ( $cb ) {
$cb();
};
$localDB = $this->createMock( IDatabase::class );
$localDB->expects( $this->once() )->method( 'update' )->with(
'abuse_filter',
[ 'af_hit_count=af_hit_count+1' ],
[ 'af_id' => $localFilters ]
);
$localDB->method( 'onTransactionPreCommitOrIdle' )->willReturnCallback( $onTransactionCB );
$lb = $this->createMock( ILoadBalancer::class );
$lb->method( 'getConnectionRef' )->willReturn( $localDB );
$globalDB = $this->createMock( IDatabase::class );
$globalDB->expects( $this->once() )->method( 'update' )->with(
'abuse_filter',
[ 'af_hit_count=af_hit_count+1' ],
[ 'af_id' => $globalFilters ]
);
$globalDB->method( 'onTransactionPreCommitOrIdle' )->willReturnCallback( $onTransactionCB );
$centralDBManager = $this->createMock( CentralDBManager::class );
$centralDBManager->method( 'getConnection' )->willReturn( $globalDB );
$watcher = new UpdateHitCountWatcher( $lb, $centralDBManager );
$watcher->run( $localFilters, $globalFilters, 'default' );
// Two soft assertions done above
$this->addToAssertionCount( 2 );
}
}