Make UserDefTest a unit test

Building on my last few changes to use UserIdentityLookup and
TitleParser, plus the recent addition of UserNameUtils to the
DummyServicesTrait, its now fairly simple to make this a unit
test and retrieve services from DummyServicesTrait instead of
MediaWikiServices.

Add a 'hookContainer' option to
DummyServicesTrait::getDummyUserNameUtils(), because
subclasses of TypeDefTestCase don't have a helper method
createHookContainer() (normally this is provided by
MediaWikiTestCaseTrait). Instead, create a manual mock
HookContainer, like we did previously at NamespaceDefTest.

Also add more options to DummyServicesTrait to allow
callers to provide service instances, needed to avoid
creating two MediaWikiTitleCodec objects and to use
a hook container in NamespaceInfo.

This also required replacing uses of createNoOpMock()
in DummyServicesTrait, because that is also not available
in UserDefTest (its another feature of MediaWikiTestCaseTrait).
It may be worth exploring splitting MediaWikiTestCaseTrait into
the parts that are specific to MediaWiki (like HookContainer
or the Message system) and parts that are useful generally
(like createNoOpMock).

Change-Id: I25b8f0256d222d994173eee66f379fb5422994b5
This commit is contained in:
DannyS712 2021-07-27 20:51:52 +00:00
parent 5da80e9abf
commit 722ae6c6e1
2 changed files with 55 additions and 64 deletions

View file

@ -205,6 +205,7 @@ trait DummyServicesTrait {
* @param array $options Supported keys:
* - validInterwikis: array of interwiki info to pass to getDummyInterwikiLookup
* - throwMockExceptions: boolean, see above
* - any of the options passed to getDummyNamespaceInfo (the same $options is passed on)
*
* @return MediaWikiTitleCodec
*/
@ -215,13 +216,10 @@ trait DummyServicesTrait {
];
$config = $options + $baseConfig;
$namespaceInfo = $this->getDummyNamespaceInfo();
$namespaceInfo = $this->getDummyNamespaceInfo( $options );
/** @var Language|MockObject $language */
$language = $this->createNoOpMock(
Language::class,
[ 'ucfirst', 'lc', 'getNsIndex', 'needsGenderDistinction', 'getNsText' ]
);
$language = $this->createMock( Language::class );
$language->method( 'ucfirst' )->willReturnCallback( 'ucfirst' );
$language->method( 'lc' )->willReturnCallback(
static function ( $str, $first ) {
@ -263,7 +261,7 @@ trait DummyServicesTrait {
$language->method( 'needsGenderDistinction' )->willReturn( false );
/** @var GenderCache|MockObject $genderCache */
$genderCache = $this->createNoOpMock( GenderCache::class );
$genderCache = $this->createMock( GenderCache::class );
$interwikiLookup = $this->getDummyInterwikiLookup( $config['validInterwikis'] );
@ -361,10 +359,17 @@ trait DummyServicesTrait {
}
/**
* @param array $options Valid keys are any of the configuration options passed, plus
* 'logger' (defaults to a NullLogger), 'validInterwikis' (defaults to 'interwiki'),
* and 'textFormatter' (defaults to a mock where the 'format' method (the only one
* used by UserNameUtils) just returns the key of the MessageValue provided)
* @param array $options Supported keys:
* - any of the configuration options used in the ServiceOptions
* - logger: logger to use, defaults to a NullLogger
* - textFormatter: ITextFormatter to use, defaults to a mock where the 'format' method
* (the only one used by UserNameUtils) just returns the key of the MessageValue provided)
* - titleParser: TitleParser to use, otherwise we will use getDummyTitleParser()
* - any of the options passed to getDummyTitleParser (the same $options is passed on if
* no titleParser is provided) (we change the default for "validInterwikis" to be
* [ 'interwiki' ] instead of an empty array if not provided)
* - hookContainer: specific HookContainer to use, default to creating an empty one via
* $this->createHookContainer()
* @return UserNameUtils
*/
private function getDummyUserNameUtils( array $options = [] ) {
@ -385,7 +390,7 @@ trait DummyServicesTrait {
// create a mock in each test. Note that the actual Language::ucfirst is a bit
// more complicated than this, but since the tests are all in English the plain
// php `ucfirst` should be enough.
$contentLang = $this->createNoOpMock( Language::class, [ 'ucfirst' ] );
$contentLang = $this->createMock( Language::class, [ 'ucfirst' ] );
$contentLang->method( 'ucfirst' )
->willReturnCallback( static function ( $str ) {
return ucfirst( $str );
@ -404,16 +409,21 @@ trait DummyServicesTrait {
);
}
// The TitleParser from DummyServicesTrait::getDummyTitleParser is really a
// MediaWikiTitleCodec object, and by passing `throwMockExceptions` we replace
// the actual creation of `MalformedTitleException`s with mocks - see
// MediaWikiTitleCodec::overrideCreateMalformedTitleExceptionCallback()
// The UserNameUtils code doesn't care about the message in the exception,
// just whether it is thrown.
$titleParser = $this->getDummyTitleParser( [
'validInterwikis' => ( $options['validInterwikis'] ?? [ 'interwiki' ] ),
'throwMockExceptions' => true,
] );
$titleParser = $options['titleParser'] ?? false;
if ( !$titleParser ) {
// The TitleParser from DummyServicesTrait::getDummyTitleParser is really a
// MediaWikiTitleCodec object, and by passing `throwMockExceptions` we replace
// the actual creation of `MalformedTitleException`s with mocks - see
// MediaWikiTitleCodec::overrideCreateMalformedTitleExceptionCallback()
// The UserNameUtils code doesn't care about the message in the exception,
// just whether it is thrown.
$titleParser = $this->getDummyTitleParser(
$options + [
'validInterwikis' => [ 'interwiki' ],
'throwMockExceptions' => true
]
);
}
return new UserNameUtils(
$serviceOptions,
@ -421,7 +431,7 @@ trait DummyServicesTrait {
$logger,
$titleParser,
$textFormatter,
$this->createHookContainer()
$options['hookContainer'] ?? $this->createHookContainer()
);
}

View file

@ -2,8 +2,8 @@
namespace MediaWiki\ParamValidator\TypeDef;
use MediaWiki\Interwiki\ClassicInterwikiLookup;
use MediaWiki\MediaWikiServices;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Tests\Unit\DummyServicesTrait;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityLookup;
use MediaWiki\User\UserIdentityValue;
@ -14,11 +14,10 @@ use Wikimedia\ParamValidator\TypeDef\TypeDefTestCase;
use Wikimedia\ParamValidator\ValidationException;
/**
* @TODO convert to a unit test, all dependencies are injected
*
* @covers MediaWiki\ParamValidator\TypeDef\UserDef
*/
class UserDefTest extends TypeDefTestCase {
use DummyServicesTrait;
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
// The UserIdentityLookup that we have knows about 5 users, with ids
@ -48,50 +47,32 @@ class UserDefTest extends TypeDefTestCase {
return null;
}
);
// DummyServicesTrait will call $this->createHookContainer() if we didn't pass
// one, but that method is only available from MediaWikiTestCaseTrait - just
// create a simple mock that doesn't do anything, because we
// don't care about hooks here
$hookContainer = $this->createMock( HookContainer::class );
$hookContainer->method( 'run' )->willReturn( true );
// We can throw mock exceptions because the UserDef code doesn't care about
// the messages in the exceptions, just if they are thrown
$titleParser = $this->getDummyTitleParser( [
'validInterwikis' => [ 'interwiki' ],
'throwMockExceptions' => true,
'hookContainer' => $hookContainer, // for the NamespaceInfo
] );
$userNameUtils = $this->getDummyUserNameUtils( [
'titleParser' => $titleParser, // don't create a new one
'hookContainer' => $hookContainer,
] );
return new UserDef(
$callbacks,
$userIdentityLookup,
MediaWikiServices::getInstance()->getTitleParser(),
MediaWikiServices::getInstance()->getUserNameUtils()
$titleParser,
$userNameUtils
);
}
private $wgInterwikiCache = null;
protected function setUp(): void {
global $wgInterwikiCache;
parent::setUp();
// We don't have MediaWikiIntegrationTestCase's methods available, so we have to do it ourself.
$this->wgInterwikiCache = $wgInterwikiCache;
$wgInterwikiCache = ClassicInterwikiLookup::buildCdbHash( [
[
'iw_prefix' => 'interwiki',
'iw_url' => 'http://example.com/',
'iw_local' => 0,
'iw_trans' => 0,
],
] );
// UserNameUtils holds TitleParser (aka _MediaWikiTitleCodec) holds InterwikiLookup
MediaWikiServices::getInstance()->resetServiceForTesting( 'InterwikiLookup' );
MediaWikiServices::getInstance()->resetServiceForTesting( '_MediaWikiTitleCodec' );
MediaWikiServices::getInstance()->resetServiceForTesting( 'TitleParser' );
MediaWikiServices::getInstance()->resetServiceForTesting( 'UserNameUtils' );
}
protected function tearDown(): void {
global $wgInterwikiCache;
$wgInterwikiCache = $this->wgInterwikiCache;
MediaWikiServices::getInstance()->resetServiceForTesting( 'InterwikiLookup' );
MediaWikiServices::getInstance()->resetServiceForTesting( '_MediaWikiTitleCodec' );
MediaWikiServices::getInstance()->resetServiceForTesting( 'TitleParser' );
MediaWikiServices::getInstance()->resetServiceForTesting( 'UserNameUtils' );
parent::tearDown();
}
public function provideValidate() {
// General tests of string inputs
$data = [