2018-07-31 16:15:17 +00:00
|
|
|
<?php
|
|
|
|
|
|
2018-08-12 09:08:58 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2020-05-25 18:47:06 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2018-08-12 09:08:58 +00:00
|
|
|
|
2018-07-31 16:15:17 +00:00
|
|
|
/**
|
|
|
|
|
* @group API
|
|
|
|
|
* @group medium
|
2018-09-14 18:30:30 +00:00
|
|
|
* @group Database
|
2018-07-31 16:15:17 +00:00
|
|
|
*
|
|
|
|
|
* @covers ApiQuerySiteinfo
|
|
|
|
|
*/
|
|
|
|
|
class ApiQuerySiteinfoTest extends ApiTestCase {
|
2020-05-25 18:47:06 +00:00
|
|
|
private $originalRegistryLoaded = null;
|
2018-07-31 16:15:17 +00:00
|
|
|
|
2021-07-22 03:11:47 +00:00
|
|
|
protected function tearDown(): void {
|
2020-05-25 18:47:06 +00:00
|
|
|
if ( $this->originalRegistryLoaded !== null ) {
|
|
|
|
|
$reg = TestingAccessWrapper::newFromObject( ExtensionRegistry::getInstance() );
|
|
|
|
|
$reg->loaded = $this->originalRegistryLoaded;
|
|
|
|
|
$this->originalRegistryLoaded = null;
|
|
|
|
|
}
|
|
|
|
|
parent::tearDown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't try to test every single thing for every category, just a sample
|
2018-07-31 16:15:17 +00:00
|
|
|
protected function doQuery( $siprop = null, $extraParams = [] ) {
|
|
|
|
|
$params = [ 'action' => 'query', 'meta' => 'siteinfo' ];
|
|
|
|
|
if ( $siprop !== null ) {
|
|
|
|
|
$params['siprop'] = $siprop;
|
|
|
|
|
}
|
|
|
|
|
$params = array_merge( $params, $extraParams );
|
|
|
|
|
|
|
|
|
|
$res = $this->doApiRequest( $params );
|
|
|
|
|
|
|
|
|
|
$this->assertArrayNotHasKey( 'warnings', $res[0] );
|
|
|
|
|
$this->assertCount( 1, $res[0]['query'] );
|
|
|
|
|
|
|
|
|
|
return $res[0]['query'][$siprop === null ? 'general' : $siprop];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGeneral() {
|
|
|
|
|
$this->setMwGlobals( [
|
|
|
|
|
'wgAllowExternalImagesFrom' => '//localhost/',
|
2019-10-04 21:08:59 +00:00
|
|
|
'wgMainPageIsDomainRoot' => true,
|
2018-07-31 16:15:17 +00:00
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$data = $this->doQuery();
|
|
|
|
|
|
|
|
|
|
$this->assertSame( Title::newMainPage()->getPrefixedText(), $data['mainpage'] );
|
|
|
|
|
$this->assertSame( PHP_VERSION, $data['phpversion'] );
|
|
|
|
|
$this->assertSame( [ '//localhost/' ], $data['externalimages'] );
|
2019-10-04 21:08:59 +00:00
|
|
|
$this->assertTrue( $data['mainpageisdomainroot'] );
|
2018-07-31 16:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLinkPrefixCharset() {
|
2019-08-26 12:24:37 +00:00
|
|
|
$contLang = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'ar' );
|
2019-02-05 03:52:00 +00:00
|
|
|
$this->setContentLang( $contLang );
|
|
|
|
|
$this->assertTrue( $contLang->linkPrefixExtension(), 'Sanity check' );
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$data = $this->doQuery();
|
|
|
|
|
|
2019-02-05 03:52:00 +00:00
|
|
|
$this->assertSame( $contLang->linkPrefixCharset(), $data['linkprefixcharset'] );
|
2018-07-31 16:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testVariants() {
|
2019-08-26 12:24:37 +00:00
|
|
|
$contLang = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'zh' );
|
2019-02-05 03:52:00 +00:00
|
|
|
$this->setContentLang( $contLang );
|
|
|
|
|
$this->assertTrue( $contLang->hasVariants(), 'Sanity check' );
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$data = $this->doQuery();
|
|
|
|
|
|
|
|
|
|
$expected = array_map(
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $code ) use ( $contLang ) {
|
2019-02-05 03:52:00 +00:00
|
|
|
return [ 'code' => $code, 'name' => $contLang->getVariantname( $code ) ];
|
2018-07-31 16:15:17 +00:00
|
|
|
},
|
2019-02-05 03:52:00 +00:00
|
|
|
$contLang->getVariants()
|
2018-07-31 16:15:17 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $data['variants'] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testReadOnly() {
|
2019-02-05 03:52:00 +00:00
|
|
|
$svc = MediaWikiServices::getInstance()->getReadOnlyMode();
|
2018-07-31 16:15:17 +00:00
|
|
|
$svc->setReason( 'Need more donations' );
|
|
|
|
|
try {
|
|
|
|
|
$data = $this->doQuery();
|
|
|
|
|
} finally {
|
|
|
|
|
$svc->setReason( false );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->assertTrue( $data['readonly'] );
|
|
|
|
|
$this->assertSame( 'Need more donations', $data['readonlyreason'] );
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 21:34:00 +00:00
|
|
|
public function testNamespacesBasic() {
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
array_keys( MediaWikiServices::getInstance()->getContentLanguage()->getFormattedNamespaces() ),
|
|
|
|
|
array_keys( $this->doQuery( 'namespaces' ) )
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-07-31 16:15:17 +00:00
|
|
|
|
2019-08-29 21:34:00 +00:00
|
|
|
public function testNamespacesExtraNS() {
|
|
|
|
|
$this->setMwGlobals( 'wgExtraNamespaces', [ '138' => 'Testing' ] );
|
2019-02-05 03:52:00 +00:00
|
|
|
$this->assertSame(
|
|
|
|
|
array_keys( MediaWikiServices::getInstance()->getContentLanguage()->getFormattedNamespaces() ),
|
|
|
|
|
array_keys( $this->doQuery( 'namespaces' ) )
|
|
|
|
|
);
|
2018-07-31 16:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
2019-08-29 21:34:00 +00:00
|
|
|
public function testNamespacesProtection() {
|
|
|
|
|
$this->setMwGlobals(
|
|
|
|
|
'wgNamespaceProtection',
|
|
|
|
|
[
|
|
|
|
|
'0' => '',
|
|
|
|
|
'2' => [ '' ],
|
|
|
|
|
'4' => 'editsemiprotected',
|
|
|
|
|
'8' => [
|
|
|
|
|
'editinterface',
|
|
|
|
|
'noratelimit'
|
|
|
|
|
],
|
|
|
|
|
'14' => [
|
|
|
|
|
'move-categorypages',
|
|
|
|
|
''
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
$data = $this->doQuery( 'namespaces' );
|
|
|
|
|
$this->assertArrayNotHasKey( 'namespaceprotection', $data['0'] );
|
|
|
|
|
$this->assertArrayNotHasKey( 'namespaceprotection', $data['2'] );
|
|
|
|
|
$this->assertSame( 'editsemiprotected', $data['4']['namespaceprotection'] );
|
|
|
|
|
$this->assertSame( 'editinterface|noratelimit', $data['8']['namespaceprotection'] );
|
|
|
|
|
$this->assertSame( 'move-categorypages', $data['14']['namespaceprotection'] );
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-31 16:15:17 +00:00
|
|
|
public function testNamespaceAliases() {
|
languages: Move default $wgNamespaceAliases to MessagesEn.php
These are not configuration but business logic, similar to the
canonical names that are in NamespaceInfo.php, these must always
exist and cannot be altered or unset.
They were previously unconditionally assigned during all requests
in Setup.php and passed down as "site configuration".
Changes:
* Move them to MessagesEn.php where they can be cached and
processed the same way as other core-provided aliases.
Document and confirm with tests that this is a mergeable
attribute that follows the language chain.
* Remove the duplicated code in a few places that was reading
this variable + Language::getNamespaceAliases(), to instead
just call the latter and move the logic there, centralised,
and tested.
In doing so I noticed that these were applied in an
inconsistent order. Sometimes the config won, sometimes not.
There's no obvious right or wrong way here, but I've chosen
to standardise on the way that Language::getNamespaceIds() did
it, which is that config wins. This because that method seems
to be most widely used of the three (it decides how URLs and
titles are parsed), and thus the one I least want to change
the behaviour of.
* Document that $wgNamespaceAliases may only be used to
define (extra) aliases, it is and never was a way to access
the complete list of aliases.
Bug: T189966
Change-Id: Ibb14181aba8c1b509264ed40523e9ab4000fd71a
2020-03-14 18:16:20 +00:00
|
|
|
$expected = MediaWikiServices::getInstance()->getContentLanguage()->getNamespaceAliases();
|
2018-07-31 16:15:17 +00:00
|
|
|
$expected = array_map(
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $key, $val ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
return [ 'id' => $val, 'alias' => strtr( $key, '_', ' ' ) ];
|
|
|
|
|
},
|
|
|
|
|
array_keys( $expected ),
|
|
|
|
|
$expected
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $this->doQuery( 'namespacealiases' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSpecialPageAliases() {
|
|
|
|
|
$this->assertCount(
|
2018-08-12 09:08:58 +00:00
|
|
|
count( MediaWikiServices::getInstance()->getSpecialPageFactory()->getNames() ),
|
2018-07-31 16:15:17 +00:00
|
|
|
$this->doQuery( 'specialpagealiases' )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testMagicWords() {
|
|
|
|
|
$this->assertCount(
|
2019-02-05 03:52:00 +00:00
|
|
|
count( MediaWikiServices::getInstance()->getContentLanguage()->getMagicWords() ),
|
2018-07-31 16:15:17 +00:00
|
|
|
$this->doQuery( 'magicwords' )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider interwikiMapProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testInterwikiMap( $filter ) {
|
2021-04-16 17:44:31 +00:00
|
|
|
$this->setMwGlobals( [
|
|
|
|
|
'wgServer' => 'https://local.example',
|
|
|
|
|
'wgScriptPath' => '/w',
|
|
|
|
|
] );
|
2018-07-31 16:15:17 +00:00
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = wfGetDB( DB_PRIMARY );
|
2018-07-31 16:15:17 +00:00
|
|
|
$dbw->insert(
|
|
|
|
|
'interwiki',
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'iw_prefix' => 'self',
|
2021-04-16 17:44:31 +00:00
|
|
|
'iw_url' => 'https://local.example/w/index.php?title=$1',
|
|
|
|
|
'iw_api' => 'https://local.example/w/api.php',
|
2018-07-31 16:15:17 +00:00
|
|
|
'iw_wikiid' => 'somedbname',
|
|
|
|
|
'iw_local' => true,
|
|
|
|
|
'iw_trans' => true,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'iw_prefix' => 'foreign',
|
|
|
|
|
'iw_url' => '//foreign.example/wiki/$1',
|
|
|
|
|
'iw_api' => '',
|
|
|
|
|
'iw_wikiid' => '',
|
|
|
|
|
'iw_local' => false,
|
|
|
|
|
'iw_trans' => false,
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
__METHOD__,
|
|
|
|
|
'IGNORE'
|
|
|
|
|
);
|
|
|
|
|
$this->tablesUsed[] = 'interwiki';
|
|
|
|
|
|
|
|
|
|
$this->setMwGlobals( [
|
|
|
|
|
'wgLocalInterwikis' => [ 'self' ],
|
|
|
|
|
'wgExtraInterlanguageLinkPrefixes' => [ 'self' ],
|
|
|
|
|
'wgExtraLanguageNames' => [ 'self' => 'Recursion' ],
|
|
|
|
|
] );
|
2019-05-02 14:23:42 +00:00
|
|
|
$this->resetServices();
|
2018-07-31 16:15:17 +00:00
|
|
|
|
2020-03-14 12:58:53 +00:00
|
|
|
MediaWikiServices::getInstance()->getMessageCache()->enable();
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$this->editPage( 'MediaWiki:Interlanguage-link-self', 'Self!' );
|
|
|
|
|
$this->editPage( 'MediaWiki:Interlanguage-link-sitename-self', 'Circular logic' );
|
|
|
|
|
|
|
|
|
|
$expected = [];
|
|
|
|
|
|
|
|
|
|
if ( $filter === null || $filter === '!local' ) {
|
|
|
|
|
$expected[] = [
|
|
|
|
|
'prefix' => 'foreign',
|
2021-04-16 17:44:31 +00:00
|
|
|
'url' => 'http://foreign.example/wiki/$1',
|
2018-07-31 16:15:17 +00:00
|
|
|
'protorel' => true,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
if ( $filter === null || $filter === 'local' ) {
|
|
|
|
|
$expected[] = [
|
|
|
|
|
'prefix' => 'self',
|
|
|
|
|
'local' => true,
|
|
|
|
|
'trans' => true,
|
|
|
|
|
'language' => 'Recursion',
|
|
|
|
|
'localinterwiki' => true,
|
|
|
|
|
'extralanglink' => true,
|
|
|
|
|
'linktext' => 'Self!',
|
|
|
|
|
'sitename' => 'Circular logic',
|
2021-04-16 17:44:31 +00:00
|
|
|
'url' => 'https://local.example/w/index.php?title=$1',
|
2018-07-31 16:15:17 +00:00
|
|
|
'protorel' => false,
|
|
|
|
|
'wikiid' => 'somedbname',
|
2021-04-16 17:44:31 +00:00
|
|
|
'api' => 'https://local.example/w/api.php',
|
2018-07-31 16:15:17 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $this->doQuery( 'interwikimap',
|
|
|
|
|
$filter === null ? [] : [ 'sifilteriw' => $filter ] );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $data );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function interwikiMapProvider() {
|
|
|
|
|
return [ [ 'local' ], [ '!local' ], [ null ] ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider dbReplLagProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testDbReplLagInfo( $showHostnames, $includeAll ) {
|
|
|
|
|
if ( !$showHostnames && $includeAll ) {
|
|
|
|
|
$this->setExpectedApiException( 'apierror-siteinfo-includealldenied' );
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-26 08:17:59 +00:00
|
|
|
$mockLB = $this->createMock( LoadBalancer::class );
|
2018-07-31 16:15:17 +00:00
|
|
|
$mockLB->method( 'getMaxLag' )->willReturn( [ null, 7, 1 ] );
|
|
|
|
|
$mockLB->method( 'getLagTimes' )->willReturn( [ 5, 7 ] );
|
|
|
|
|
$mockLB->method( 'getServerName' )->will( $this->returnValueMap( [
|
|
|
|
|
[ 0, 'apple' ], [ 1, 'carrot' ]
|
|
|
|
|
] ) );
|
2019-08-26 08:17:59 +00:00
|
|
|
$mockLB->method( 'getLocalDomainID' )->willReturn( 'testdomain' );
|
|
|
|
|
$mockLB->expects( $this->never() )->method( $this->anythingBut(
|
|
|
|
|
'getMaxLag', 'getLagTimes', 'getServerName', 'getLocalDomainID', '__destruct'
|
|
|
|
|
) );
|
2018-07-31 16:15:17 +00:00
|
|
|
$this->setService( 'DBLoadBalancer', $mockLB );
|
|
|
|
|
|
|
|
|
|
$this->setMwGlobals( 'wgShowHostnames', $showHostnames );
|
|
|
|
|
|
|
|
|
|
$expected = [];
|
|
|
|
|
if ( $includeAll ) {
|
|
|
|
|
$expected[] = [ 'host' => $showHostnames ? 'apple' : '', 'lag' => 5 ];
|
|
|
|
|
}
|
|
|
|
|
$expected[] = [ 'host' => $showHostnames ? 'carrot' : '', 'lag' => 7 ];
|
|
|
|
|
|
|
|
|
|
$data = $this->doQuery( 'dbrepllag', $includeAll ? [ 'sishowalldb' => '' ] : [] );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $data );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function dbReplLagProvider() {
|
|
|
|
|
return [
|
|
|
|
|
'no hostnames, no showalldb' => [ false, false ],
|
|
|
|
|
'no hostnames, showalldb' => [ false, true ],
|
|
|
|
|
'hostnames, no showalldb' => [ true, false ],
|
|
|
|
|
'hostnames, showalldb' => [ true, true ]
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testStatistics() {
|
|
|
|
|
$this->setTemporaryHook( 'APIQuerySiteInfoStatisticsInfo',
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( &$data ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
$data['addedstats'] = 42;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
|
'pages' => intval( SiteStats::pages() ),
|
|
|
|
|
'articles' => intval( SiteStats::articles() ),
|
|
|
|
|
'edits' => intval( SiteStats::edits() ),
|
|
|
|
|
'images' => intval( SiteStats::images() ),
|
|
|
|
|
'users' => intval( SiteStats::users() ),
|
|
|
|
|
'activeusers' => intval( SiteStats::activeUsers() ),
|
|
|
|
|
'admins' => intval( SiteStats::numberingroup( 'sysop' ) ),
|
|
|
|
|
'jobs' => intval( SiteStats::jobs() ),
|
|
|
|
|
'addedstats' => 42,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $this->doQuery( 'statistics' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider groupsProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testUserGroups( $numInGroup ) {
|
|
|
|
|
global $wgGroupPermissions, $wgAutopromote;
|
|
|
|
|
|
|
|
|
|
$this->setGroupPermissions( 'viscount', 'perambulate', 'yes' );
|
|
|
|
|
$this->setGroupPermissions( 'viscount', 'legislate', '0' );
|
|
|
|
|
$this->setMwGlobals( [
|
|
|
|
|
'wgAddGroups' => [ 'viscount' => true, 'bot' => [] ],
|
|
|
|
|
'wgRemoveGroups' => [ 'viscount' => [ 'sysop' ], 'bot' => [ '*', 'earl' ] ],
|
|
|
|
|
'wgGroupsAddToSelf' => [ 'bot' => [ 'bureaucrat', 'sysop' ] ],
|
|
|
|
|
'wgGroupsRemoveFromSelf' => [ 'bot' => [ 'bot' ] ],
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$data = $this->doQuery( 'usergroups', $numInGroup ? [ 'sinumberingroup' => '' ] : [] );
|
|
|
|
|
|
|
|
|
|
$names = array_map(
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $val ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
return $val['name'];
|
|
|
|
|
},
|
|
|
|
|
$data
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( array_keys( $wgGroupPermissions ), $names );
|
|
|
|
|
|
|
|
|
|
foreach ( $data as $val ) {
|
|
|
|
|
if ( !$numInGroup ) {
|
|
|
|
|
$expectedSize = null;
|
|
|
|
|
} elseif ( $val['name'] === 'user' ) {
|
|
|
|
|
$expectedSize = SiteStats::users();
|
|
|
|
|
} elseif ( $val['name'] === '*' || isset( $wgAutopromote[$val['name']] ) ) {
|
|
|
|
|
$expectedSize = null;
|
|
|
|
|
} else {
|
|
|
|
|
$expectedSize = SiteStats::numberingroup( $val['name'] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $expectedSize === null ) {
|
|
|
|
|
$this->assertArrayNotHasKey( 'number', $val );
|
|
|
|
|
} else {
|
|
|
|
|
$this->assertSame( $expectedSize, $val['number'] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $val['name'] === 'viscount' ) {
|
|
|
|
|
$viscountFound = true;
|
|
|
|
|
$this->assertSame( [ 'perambulate' ], $val['rights'] );
|
|
|
|
|
$this->assertSame( User::getAllGroups(), $val['add'] );
|
|
|
|
|
} elseif ( $val['name'] === 'bot' ) {
|
|
|
|
|
$this->assertArrayNotHasKey( 'add', $val );
|
|
|
|
|
$this->assertArrayNotHasKey( 'remove', $val );
|
|
|
|
|
$this->assertSame( [ 'bureaucrat', 'sysop' ], $val['add-self'] );
|
|
|
|
|
$this->assertSame( [ 'bot' ], $val['remove-self'] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testFileExtensions() {
|
|
|
|
|
// Add duplicate
|
2021-04-16 17:44:31 +00:00
|
|
|
$this->setMwGlobals( 'wgFileExtensions', [ 'png', 'gif', 'jpg', 'png' ] );
|
2018-07-31 16:15:17 +00:00
|
|
|
|
2021-04-16 17:44:31 +00:00
|
|
|
$expected = [ [ 'ext' => 'png' ], [ 'ext' => 'gif' ], [ 'ext' => 'jpg' ] ];
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $this->doQuery( 'fileextensions' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function groupsProvider() {
|
|
|
|
|
return [
|
|
|
|
|
'numingroup' => [ true ],
|
|
|
|
|
'nonumingroup' => [ false ],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testInstalledLibraries() {
|
|
|
|
|
// @todo Test no installed.json? Moving installed.json to a different name temporarily
|
|
|
|
|
// seems a bit scary, but I don't see any other way to do it.
|
|
|
|
|
//
|
|
|
|
|
// @todo Install extensions/skins somehow so that we can test they're filtered out
|
|
|
|
|
global $IP;
|
|
|
|
|
|
|
|
|
|
$path = "$IP/vendor/composer/installed.json";
|
|
|
|
|
if ( !file_exists( $path ) ) {
|
|
|
|
|
$this->markTestSkipped( 'No installed libraries' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$expected = ( new ComposerInstalled( $path ) )->getInstalledDependencies();
|
|
|
|
|
|
|
|
|
|
$expected = array_filter( $expected,
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $info ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
return strpos( $info['type'], 'mediawiki-' ) !== 0;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$expected = array_map(
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $name, $info ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
return [ 'name' => $name, 'version' => $info['version'] ];
|
|
|
|
|
},
|
|
|
|
|
array_keys( $expected ),
|
|
|
|
|
array_values( $expected )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $this->doQuery( 'libraries' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testExtensions() {
|
|
|
|
|
$tmpdir = $this->getNewTempDirectory();
|
|
|
|
|
touch( "$tmpdir/ErsatzExtension.php" );
|
|
|
|
|
touch( "$tmpdir/LICENSE" );
|
|
|
|
|
touch( "$tmpdir/AUTHORS.txt" );
|
|
|
|
|
|
|
|
|
|
$val = [
|
|
|
|
|
'path' => "$tmpdir/ErsatzExtension.php",
|
|
|
|
|
'name' => 'Ersatz Extension',
|
|
|
|
|
'namemsg' => 'ersatz-extension-name',
|
|
|
|
|
'author' => 'John Smith',
|
|
|
|
|
'version' => '0.0.2',
|
|
|
|
|
'url' => 'https://www.example.com/software/ersatz-extension',
|
|
|
|
|
'description' => 'An extension that is not what it seems.',
|
|
|
|
|
'descriptionmsg' => 'ersatz-extension-desc',
|
|
|
|
|
'license-name' => 'PD',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->setMwGlobals( 'wgExtensionCredits', [ 'api' => [
|
|
|
|
|
$val,
|
|
|
|
|
[
|
|
|
|
|
'author' => [ 'John Smith', 'John Smith Jr.', '...' ],
|
|
|
|
|
'descriptionmsg' => [ 'another-extension-desc', 'param' ] ],
|
|
|
|
|
] ] );
|
2020-05-25 18:47:06 +00:00
|
|
|
// Make the main registry empty
|
|
|
|
|
// TODO: Make ExtensionRegistry an injected service?
|
|
|
|
|
$reg = TestingAccessWrapper::newFromObject( ExtensionRegistry::getInstance() );
|
|
|
|
|
$this->originalRegistryLoaded = $reg->loaded;
|
|
|
|
|
$reg->loaded = [];
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$data = $this->doQuery( 'extensions' );
|
|
|
|
|
|
|
|
|
|
$this->assertCount( 2, $data );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( 'api', $data[0]['type'] );
|
|
|
|
|
|
|
|
|
|
$sharedKeys = [ 'name', 'namemsg', 'description', 'descriptionmsg', 'author', 'url',
|
|
|
|
|
'version', 'license-name' ];
|
|
|
|
|
foreach ( $sharedKeys as $key ) {
|
|
|
|
|
$this->assertSame( $val[$key], $data[0][$key] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @todo Test git info
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
Title::newFromText( 'Special:Version/License/Ersatz Extension' )->getLinkURL(),
|
|
|
|
|
$data[0]['license']
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
Title::newFromText( 'Special:Version/Credits/Ersatz Extension' )->getLinkURL(),
|
|
|
|
|
$data[0]['credits']
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( 'another-extension-desc', $data[1]['descriptionmsg'] );
|
|
|
|
|
$this->assertSame( [ 'param' ], $data[1]['descriptionmsgparams'] );
|
|
|
|
|
$this->assertSame( 'John Smith, John Smith Jr., ...', $data[1]['author'] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider rightsInfoProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testRightsInfo( $page, $url, $text, $expectedUrl, $expectedText ) {
|
|
|
|
|
$this->setMwGlobals( [
|
|
|
|
|
'wgRightsPage' => $page,
|
|
|
|
|
'wgRightsUrl' => $url,
|
|
|
|
|
'wgRightsText' => $text,
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
[ 'url' => $expectedUrl, 'text' => $expectedText ],
|
|
|
|
|
$this->doQuery( 'rightsinfo' )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function rightsInfoProvider() {
|
|
|
|
|
$textUrl = wfExpandUrl( Title::newFromText( 'License' ), PROTO_CURRENT );
|
|
|
|
|
$url = 'http://license.example/';
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'No rights info' => [ null, null, null, '', '' ],
|
|
|
|
|
'Only page' => [ 'License', null, null, $textUrl, 'License' ],
|
|
|
|
|
'Only URL' => [ null, $url, null, $url, '' ],
|
|
|
|
|
'Only text' => [ null, null, '!!!', '', '!!!' ],
|
|
|
|
|
// URL is ignored if page is specified
|
|
|
|
|
'Page and URL' => [ 'License', $url, null, $textUrl, 'License' ],
|
|
|
|
|
'URL and text' => [ null, $url, '!!!', $url, '!!!' ],
|
|
|
|
|
'Page and text' => [ 'License', null, '!!!', $textUrl, '!!!' ],
|
|
|
|
|
'Page and URL and text' => [ 'License', $url, '!!!', $textUrl, '!!!' ],
|
|
|
|
|
'Pagename "0"' => [ '0', null, null,
|
|
|
|
|
wfExpandUrl( Title::newFromText( '0' ), PROTO_CURRENT ), '0' ],
|
|
|
|
|
'URL "0"' => [ null, '0', null, '0', '' ],
|
|
|
|
|
'Text "0"' => [ null, null, '0', '', '0' ],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRestrictions() {
|
|
|
|
|
global $wgRestrictionTypes, $wgRestrictionLevels, $wgCascadingRestrictionLevels,
|
|
|
|
|
$wgSemiprotectedRestrictionLevels;
|
|
|
|
|
|
|
|
|
|
$this->assertSame( [
|
|
|
|
|
'types' => $wgRestrictionTypes,
|
|
|
|
|
'levels' => $wgRestrictionLevels,
|
|
|
|
|
'cascadinglevels' => $wgCascadingRestrictionLevels,
|
|
|
|
|
'semiprotectedlevels' => $wgSemiprotectedRestrictionLevels,
|
|
|
|
|
], $this->doQuery( 'restrictions' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider languagesProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testLanguages( $langCode ) {
|
2020-01-03 23:03:14 +00:00
|
|
|
$expected = MediaWikiServices::getInstance()
|
|
|
|
|
->getLanguageNameUtils()
|
|
|
|
|
->getLanguageNames( (string)$langCode );
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$expected = array_map(
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $code, $name ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
return [
|
|
|
|
|
'code' => $code,
|
2018-06-26 20:39:57 +00:00
|
|
|
'bcp47' => LanguageCode::bcp47( $code ),
|
2018-07-31 16:15:17 +00:00
|
|
|
'name' => $name
|
|
|
|
|
];
|
|
|
|
|
},
|
|
|
|
|
array_keys( $expected ),
|
|
|
|
|
array_values( $expected )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$data = $this->doQuery( 'languages',
|
|
|
|
|
$langCode !== null ? [ 'siinlanguagecode' => $langCode ] : [] );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $data );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function languagesProvider() {
|
|
|
|
|
return [ [ null ], [ 'fr' ] ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLanguageVariants() {
|
|
|
|
|
$expectedKeys = array_filter( LanguageConverter::$languagesWithVariants,
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $langCode ) {
|
2020-01-23 18:39:23 +00:00
|
|
|
$lang = MediaWikiServices::getInstance()->getLanguageFactory()
|
|
|
|
|
->getLanguage( $langCode );
|
|
|
|
|
$converter = MediaWikiServices::getInstance()->getLanguageConverterFactory()
|
|
|
|
|
->getLanguageConverter( $lang );
|
|
|
|
|
return $converter->hasVariants();
|
2018-07-31 16:15:17 +00:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
sort( $expectedKeys );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expectedKeys, array_keys( $this->doQuery( 'languagevariants' ) ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLanguageVariantsDisabled() {
|
|
|
|
|
$this->setMwGlobals( 'wgDisableLangConversion', true );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( [], $this->doQuery( 'languagevariants' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @todo Test a skin with a description that's known to be different in a different language.
|
|
|
|
|
* Vector will do, but it's not installed by default.
|
|
|
|
|
*
|
|
|
|
|
* @todo Test that an invalid language code doesn't actually try reading any messages
|
|
|
|
|
*
|
|
|
|
|
* @dataProvider skinsProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testSkins( $code ) {
|
|
|
|
|
$data = $this->doQuery( 'skins', $code !== null ? [ 'siinlanguagecode' => $code ] : [] );
|
2020-07-15 05:28:37 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
$skinFactory = $services->getSkinFactory();
|
|
|
|
|
$skinNames = $skinFactory->getSkinNames();
|
|
|
|
|
$expectedAllowed = $skinFactory->getAllowedSkins();
|
2018-07-31 16:15:17 +00:00
|
|
|
$expectedDefault = Skin::normalizeKey( 'default' );
|
2020-07-15 05:28:37 +00:00
|
|
|
$languageNameUtils = $services->getLanguageNameUtils();
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$i = 0;
|
2020-07-15 05:28:37 +00:00
|
|
|
foreach ( $skinNames as $name => $displayName ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
$this->assertSame( $name, $data[$i]['code'] );
|
|
|
|
|
|
|
|
|
|
$msg = wfMessage( "skinname-$name" );
|
2020-01-03 23:03:14 +00:00
|
|
|
if ( $code && $languageNameUtils->isValidCode( $code ) ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
$msg->inLanguage( $code );
|
|
|
|
|
} else {
|
|
|
|
|
$msg->inContentLanguage();
|
|
|
|
|
}
|
|
|
|
|
if ( $msg->exists() ) {
|
|
|
|
|
$displayName = $msg->text();
|
|
|
|
|
}
|
|
|
|
|
$this->assertSame( $displayName, $data[$i]['name'] );
|
|
|
|
|
|
|
|
|
|
if ( !isset( $expectedAllowed[$name] ) ) {
|
|
|
|
|
$this->assertTrue( $data[$i]['unusable'], "$name must be unusable" );
|
|
|
|
|
}
|
|
|
|
|
if ( $name === $expectedDefault ) {
|
|
|
|
|
$this->assertTrue( $data[$i]['default'], "$expectedDefault must be default" );
|
|
|
|
|
}
|
|
|
|
|
$i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function skinsProvider() {
|
|
|
|
|
return [
|
|
|
|
|
'No language specified' => [ null ],
|
|
|
|
|
'Czech' => [ 'cs' ],
|
|
|
|
|
'Invalid language' => [ '/invalid/' ],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testExtensionTags() {
|
|
|
|
|
$expected = array_map(
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $tag ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
return "<$tag>";
|
|
|
|
|
},
|
2019-04-11 13:36:15 +00:00
|
|
|
MediaWikiServices::getInstance()->getParser()->getTags()
|
2018-07-31 16:15:17 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $expected, $this->doQuery( 'extensiontags' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testFunctionHooks() {
|
2019-04-11 13:36:15 +00:00
|
|
|
$this->assertSame( MediaWikiServices::getInstance()->getParser()->getFunctionHooks(),
|
|
|
|
|
$this->doQuery( 'functionhooks' ) );
|
2018-07-31 16:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testVariables() {
|
2018-07-30 14:57:53 +00:00
|
|
|
$this->assertSame(
|
|
|
|
|
MediaWikiServices::getInstance()->getMagicWordFactory()->getVariableIDs(),
|
|
|
|
|
$this->doQuery( 'variables' )
|
|
|
|
|
);
|
2018-07-31 16:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testProtocols() {
|
|
|
|
|
global $wgUrlProtocols;
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $wgUrlProtocols, $this->doQuery( 'protocols' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testDefaultOptions() {
|
2021-03-16 15:16:18 +00:00
|
|
|
$this->assertSame(
|
|
|
|
|
$this->getServiceContainer()->getUserOptionsLookup()->getDefaultOptions(),
|
|
|
|
|
$this->doQuery( 'defaultoptions' )
|
|
|
|
|
);
|
2018-07-31 16:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testUploadDialog() {
|
|
|
|
|
global $wgUploadDialog;
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $wgUploadDialog, $this->doQuery( 'uploaddialog' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetHooks() {
|
|
|
|
|
global $wgHooks;
|
|
|
|
|
|
|
|
|
|
// Make sure there's something to report on
|
|
|
|
|
$this->setTemporaryHook( 'somehook',
|
2021-02-07 13:10:36 +00:00
|
|
|
static function () {
|
2018-07-31 16:15:17 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$expectedNames = $wgHooks;
|
|
|
|
|
ksort( $expectedNames );
|
|
|
|
|
|
|
|
|
|
$actualNames = array_map(
|
2021-02-06 19:40:52 +00:00
|
|
|
static function ( $val ) {
|
2018-07-31 16:15:17 +00:00
|
|
|
return $val['name'];
|
|
|
|
|
},
|
|
|
|
|
$this->doQuery( 'showhooks' )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( array_keys( $expectedNames ), $actualNames );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testContinuation() {
|
2021-04-16 17:44:31 +00:00
|
|
|
// Use $wgUrlProtocols as easy example for forging the
|
|
|
|
|
// size of the API response
|
2019-09-27 08:42:56 +00:00
|
|
|
$protocol = 'foo://';
|
2021-04-16 17:44:31 +00:00
|
|
|
$size = strlen( $protocol );
|
|
|
|
|
$protocols = [ $protocol ];
|
2018-07-31 16:15:17 +00:00
|
|
|
|
2021-04-16 17:44:31 +00:00
|
|
|
$this->setMwGlobals( 'wgUrlProtocols', $protocols );
|
|
|
|
|
$this->setMwGlobals( 'wgAPIMaxResultSize', $size );
|
2018-07-31 16:15:17 +00:00
|
|
|
|
|
|
|
|
$res = $this->doApiRequest( [
|
|
|
|
|
'action' => 'query',
|
|
|
|
|
'meta' => 'siteinfo',
|
|
|
|
|
'siprop' => 'protocols|languages',
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
2021-04-16 17:44:31 +00:00
|
|
|
wfMessage( 'apiwarn-truncatedresult', Message::numParam( $size ) )
|
2018-07-31 16:15:17 +00:00
|
|
|
->text(),
|
|
|
|
|
$res[0]['warnings']['result']['warnings']
|
|
|
|
|
);
|
|
|
|
|
|
2021-04-16 17:44:31 +00:00
|
|
|
$this->assertSame( $protocols, $res[0]['query']['protocols'] );
|
2018-07-31 16:15:17 +00:00
|
|
|
$this->assertArrayNotHasKey( 'languages', $res[0] );
|
|
|
|
|
$this->assertTrue( $res[0]['batchcomplete'], 'batchcomplete should be true' );
|
|
|
|
|
$this->assertSame( [ 'siprop' => 'languages', 'continue' => '-||' ], $res[0]['continue'] );
|
|
|
|
|
}
|
|
|
|
|
}
|