diff options
author | Tim Starling <tstarling@wikimedia.org> | 2025-01-15 15:25:49 +1100 |
---|---|---|
committer | Tim Starling <tstarling@wikimedia.org> | 2025-02-19 11:18:41 +1100 |
commit | c0dd3afe639260f05b89db1e1191adba9e22ac46 (patch) | |
tree | f012264484f33b826e8fe577629591483f967e9a /tests/phpunit/integration | |
parent | 8210d146e1ac8fa22586bf8412df39059631efa2 (diff) | |
download | mediawikicore-c0dd3afe639260f05b89db1e1191adba9e22ac46.tar.gz mediawikicore-c0dd3afe639260f05b89db1e1191adba9e22ac46.zip |
block: Add a BlockTarget class hierarchy
Main change:
* Add a class hierarchy representing block targets, representing a
target and type.
* Add BlockTargetFactory, replacing BlockUtils.
* Add CrossWikiBlockTargetFactory, replacing BlockUtilsFactory.
* Construct a BlockTarget object early in the request flow and pass it
down through the layers, instead of having every layer interpret
UserIdentity|string target specifications.
Also:
* Remove Block::TYPE_ID. Nothing uses it in code search, so there's
no point in porting it to the new system.
* Stop using the type constants as specificity scores. Add
BlockTarget::getSpecificity().
* Add DatabaseBlockStore::newUnsaved() to replace direct construction of
DatabaseBlock in insertBlock() callers. There are many such callers in
tests. This is part of the effort to remove the service container
usage in DatabaseBlock::__construct().
* Make DatabaseBlock::getRangeStart() and getRangeEnd() return null if
the block is not a range, since that is convenient for their only
caller following the resolution of T51504.
* Add DatabaseBlock::getIpHex() which similarly maps to a DB field in
the new schema.
* In ApiBlock and ApiUnblock, have ParamValidator provide UserIdentity
objects instead of converting to a string and back to a UserIdentity
again.
Bug: T382106
Change-Id: I2ce1a82f3fbb3cf18aa2d17986d46dbdcc70c761
Diffstat (limited to 'tests/phpunit/integration')
-rw-r--r-- | tests/phpunit/integration/includes/block/BlockTargetFactoryTest.php | 216 | ||||
-rw-r--r-- | tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php | 2 |
2 files changed, 217 insertions, 1 deletions
diff --git a/tests/phpunit/integration/includes/block/BlockTargetFactoryTest.php b/tests/phpunit/integration/includes/block/BlockTargetFactoryTest.php new file mode 100644 index 000000000000..6eded3e76258 --- /dev/null +++ b/tests/phpunit/integration/includes/block/BlockTargetFactoryTest.php @@ -0,0 +1,216 @@ +<?php + +use MediaWiki\Block\AnonIpBlockTarget; +use MediaWiki\Block\AutoBlockTarget; +use MediaWiki\Block\BlockTargetFactory; +use MediaWiki\Block\RangeBlockTarget; +use MediaWiki\Block\UserBlockTarget; +use MediaWiki\Config\ServiceOptions; +use MediaWiki\MainConfigNames; +use MediaWiki\User\UserIdentity; +use MediaWiki\User\UserIdentityLookup; +use MediaWiki\User\UserIdentityValue; +use MediaWiki\User\UserSelectQueryBuilder; +use Wikimedia\Rdbms\IDBAccessObject; + +/** + * @covers \MediaWiki\Block\BlockTargetFactory + */ +class BlockTargetFactoryTest extends \MediaWikiIntegrationTestCase { + private function getBlockTargetFactory( $wikiId = false ) { + return new BlockTargetFactory( + new ServiceOptions( + BlockTargetFactory::CONSTRUCTOR_OPTIONS, + [ + MainConfigNames::BlockCIDRLimit => [ + 'IPv4' => 16, + 'IPv6' => 19, + ], + ] + ), + $this->getMockUserIdentityLookup(), + $this->getServiceContainer()->getUserNameUtils(), + $wikiId + ); + } + + private function getMockUserIdentityLookup() { + return new class implements UserIdentityLookup { + public function getUserIdentityByName( + string $name, int $queryFlags = IDBAccessObject::READ_NORMAL + ): ?UserIdentity { + if ( $name === 'Exists' ) { + return new UserIdentityValue( 1, $name ); + } else { + return null; + } + } + + public function getUserIdentityByUserId( + int $userId, int $queryFlags = IDBAccessObject::READ_NORMAL + ): ?UserIdentity { + if ( $userId === 1 ) { + return new UserIdentityValue( 1, 'Exists' ); + } else { + return null; + } + } + + public function newSelectQueryBuilder( $dbOrQueryFlags = IDBAccessObject::READ_NORMAL + ): UserSelectQueryBuilder { + throw new RuntimeException( 'unimplemented' ); + } + }; + } + + public function testGetWikiId() { + $this->assertSame( 'enwiki', + $this->getBlockTargetFactory( 'enwiki' )->getWikiId() ); + } + + public static function provideNewFromString() { + return [ + [ '1.2.3.4', AnonIpBlockTarget::class ], + [ '::1', AnonIpBlockTarget::class, '0:0:0:0:0:0:0:1' ], + [ ' 1.2.3.4', AnonIpBlockTarget::class, '1.2.3.4' ], + [ '1.2.3.0/24', RangeBlockTarget::class ], + [ '::1/64', RangeBlockTarget::class, '0:0:0:0:0:0:0:0/64' ], + [ 'Exists', UserBlockTarget::class ], + [ 'Nonexistent', UserBlockTarget::class ], + [ '#1234', AutoBlockTarget::class ], + [ '__', null ], + [ '', null ], + [ null, null ] + ]; + } + + /** + * @dataProvider provideNewFromString + * @param string $input + * @param string $class + * @param string|null $serialized + */ + public function testNewFromString( $input, $class, $serialized = null ) { + $target = $this->getBlockTargetFactory()->newFromString( $input ); + if ( $class === null ) { + $this->assertNull( $target ); + return; + } + $this->assertInstanceOf( $class, $target ); + // Confirm round trip + $this->assertSame( $serialized ?? $input, $target->toString() ); + } + + /** + * @covers \MediaWiki\Block\UserBlockTarget::validateForCreation + */ + public function testNoSuchUser() { + $target = $this->getBlockTargetFactory()->newFromString( 'Nonexistent' ); + $status = $target->validateForCreation(); + $this->assertStatusError( 'nosuchusershort', $status ); + $this->assertSame( '<text>Nonexistent</text>', + $status->getMessages()[0]->getParams()[0]->dump() ); + } + + public static function provideNewFromUser() { + return [ + [ 1, 'Alice', UserBlockTarget::class ], + [ 5, 'Exists', UserBlockTarget::class ], + [ 0, 'Nonexistent', UserBlockTarget::class ], + [ 0, '127.0.0.1', AnonIpBlockTarget::class ], + [ 0, '1.2.3.0/24', RangeBlockTarget::class ], + ]; + } + + /** + * @dataProvider provideNewFromUser + * @param int $id + * @param string $name + * @param string $class + */ + public function testNewFromUser( $id, $name, $class ) { + $user = new UserIdentityValue( $id, $name ); + $target = $this->getBlockTargetFactory()->newFromUser( $user ); + $this->assertInstanceOf( $class, $target ); + $this->assertSame( $name, $target->toString() ); + } + + public static function provideNewFromRow() { + $default = [ + 'bt_auto' => '0', + 'bt_address' => '127.0.0.1', + 'bt_user' => null, + 'bt_user_text' => null, + 'bl_id' => 5, + ]; + return [ + 'IP block' => [ + $default, + false, + AnonIpBlockTarget::class, + '127.0.0.1', + ], + 'Autoblock exposed' => [ + [ 'bt_auto' => '1' ] + $default, + false, + AnonIpBlockTarget::class, + '127.0.0.1', + ], + 'Autoblock redacted' => [ + [ 'bt_auto' => '1' ] + $default, + true, + AutoBlockTarget::class, + '#5', + ], + 'Range block' => [ + [ 'bt_address' => '127.0.0.0/24' ] + $default, + false, + RangeBlockTarget::class, + '127.0.0.0/24', + ], + 'IPv6 range block' => [ + [ 'bt_address' => '2001:0:0:0:0:0:0:0/19' ] + $default, + false, + RangeBlockTarget::class, + '2001:0:0:0:0:0:0:0/19', + ], + 'User block' => [ + [ 'bt_user' => 2, 'bt_user_text' => 'Bob' ] + $default, + false, + UserBlockTarget::class, + 'Bob' + ], + 'Error case 1' => [ + [ 'bt_address' => null ] + $default, + false, + null, + '' + ], + 'Error case 2' => [ + [ 'bt_address' => 'Some invalid string' ] + $default, + false, + null, + '' + ], + ]; + } + + /** + * @dataProvider provideNewFromRow + * @param array $data + * @param bool $redact + * @param string|null $class + * @param string $serialized + */ + public function testNewFromRow( $data, $redact, $class, $serialized ) { + $factory = $this->getBlockTargetFactory(); + $target = $redact ? $factory->newFromRowRedacted( (object)$data ) + : $factory->newFromRowRaw( (object)$data ); + if ( $class === null ) { + $this->assertNull( $target ); + return; + } + $this->assertInstanceOf( $class, $target ); + $this->assertSame( $serialized, $target->toString() ); + } +} diff --git a/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php b/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php index 93fb77007b9f..c2e51ff01ea2 100644 --- a/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php +++ b/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php @@ -75,7 +75,7 @@ class DatabaseBlockStoreTest extends MediaWikiIntegrationTestCase { 'readOnlyMode' => $readOnlyMode, 'userFactory' => $services->getUserFactory(), 'tempUserConfig' => $services->getTempUserConfig(), - 'blockUtils' => $services->getBlockUtils(), + 'blockTargetFactory' => $services->getBlockTargetFactory(), 'autoblockExemptionList' => $services->getAutoblockExemptionList(), ]; $constructorArgs = array_merge( $defaultConstructorArgs, $overrideConstructorArgs ); |