This is inconsistent with the access pattern of other constants in MediaWiki. it's also confusing (e.g. it's unclear to a newcomer why UserFactory is implementing IDBAccessObject) and it's prone to clashes (e.g. BagOStuff class has a clashing constant). It has been already announced: https://w.wiki/9DAX Bug: T354194 Change-Id: Ic2357634b8385d65b55db2b557191419b06c40e0
782 lines
24 KiB
PHP
782 lines
24 KiB
PHP
<?php
|
|
namespace MediaWiki\Tests\Page;
|
|
|
|
use Exception;
|
|
use IDBAccessObject;
|
|
use InvalidArgumentException;
|
|
use LinkCacheTestTrait;
|
|
use MediaWiki\Config\ServiceOptions;
|
|
use MediaWiki\DAO\WikiAwareEntity;
|
|
use MediaWiki\MainConfigNames;
|
|
use MediaWiki\Page\PageIdentity;
|
|
use MediaWiki\Page\PageIdentityValue;
|
|
use MediaWiki\Page\PageRecord;
|
|
use MediaWiki\Page\PageStore;
|
|
use MediaWiki\Title\Title;
|
|
use MediaWiki\Title\TitleValue;
|
|
use MediaWikiIntegrationTestCase;
|
|
use MockTitleTrait;
|
|
use Wikimedia\Assert\PreconditionException;
|
|
use Wikimedia\Rdbms\IDatabase;
|
|
use Wikimedia\Rdbms\LoadBalancer;
|
|
|
|
/**
|
|
* @group Database
|
|
*/
|
|
class PageStoreTest extends MediaWikiIntegrationTestCase {
|
|
|
|
use MockTitleTrait;
|
|
use LinkCacheTestTrait;
|
|
|
|
/**
|
|
* @param array $options
|
|
* @param array $params
|
|
*
|
|
* @return PageStore
|
|
* @throws Exception
|
|
*/
|
|
private function getPageStore( $options = [], $params = [] ) {
|
|
$services = $this->getServiceContainer();
|
|
|
|
$serviceOptions = new ServiceOptions(
|
|
PageStore::CONSTRUCTOR_OPTIONS,
|
|
$options + [
|
|
'LanguageCode' => $services->getContentLanguage()->getCode(),
|
|
'PageLanguageUseDB' => true
|
|
]
|
|
);
|
|
|
|
return new PageStore(
|
|
$serviceOptions,
|
|
$params['dbLoadBalancer'] ?? $services->getDBLoadBalancer(),
|
|
$services->getNamespaceInfo(),
|
|
$services->getTitleParser(),
|
|
array_key_exists( 'linkCache', $params )
|
|
? $params['linkCache']
|
|
: $services->getLinkCache(),
|
|
$services->getStatsdDataFactory(),
|
|
$params['wikiId'] ?? WikiAwareEntity::LOCAL
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param PageIdentity $expected
|
|
* @param PageIdentity $actual
|
|
*/
|
|
private function assertSamePage( PageIdentity $expected, PageIdentity $actual ) {
|
|
// NOTE: Leave it to the caller to compare the wiki IDs. $expected may be local
|
|
// even if $actual belongs to a (pretend) sister site.
|
|
$expWiki = $expected->getWikiId();
|
|
$actWiki = $actual->getWikiId();
|
|
|
|
$this->assertSame( $expected->getId( $expWiki ), $actual->getId( $actWiki ) );
|
|
$this->assertSame( $expected->getNamespace(), $actual->getNamespace() );
|
|
$this->assertSame( $expected->getDBkey(), $actual->getDBkey() );
|
|
|
|
if ( $expected instanceof PageRecord ) {
|
|
$this->assertInstanceOf( PageRecord::class, $actual );
|
|
|
|
/** @var PageRecord $actual */
|
|
$this->assertSame( $expected->getLatest( $expWiki ), $actual->getLatest( $actWiki ) );
|
|
$this->assertSame( $expected->getLanguage(), $actual->getLanguage() );
|
|
$this->assertSame( $expected->getTouched(), $actual->getTouched() );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageIdentity for a link referencing a non-existing page
|
|
* @covers \MediaWiki\Page\PageStore::getPageForLink
|
|
*/
|
|
public function testGetPageForLink_nonexisting() {
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$page = $pageStore->getPageForLink( $nonexistingPage->getTitle() );
|
|
|
|
$this->assertTrue( $page->canExist() );
|
|
$this->assertFalse( $page->exists() );
|
|
|
|
$this->assertSamePage( $nonexistingPage->getTitle(), $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord for a link to an existing page.
|
|
* @covers \MediaWiki\Page\PageStore::getPageForLink
|
|
*/
|
|
public function testGetPageForLink_existing() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$page = $pageStore->getPageForLink( $existingPage->getTitle() );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertInstanceOf( PageRecord::class, $page );
|
|
$this->assertSamePage( $existingPage->toPageRecord(), $page );
|
|
}
|
|
|
|
/**
|
|
* Test that getPageForLink() can get a PageIdentity from another wiki
|
|
* @covers \MediaWiki\Page\PageStore::getPageForLink
|
|
*/
|
|
public function testGetPageForLink_crossWiki() {
|
|
$wikiId = $this->db->getDomainID(); // pretend sister site
|
|
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$pageStore = $this->getPageStore( [], [ 'wikiId' => $wikiId, 'linkCache' => null ] );
|
|
|
|
$page = $pageStore->getPageForLink( $nonexistingPage->getTitle() );
|
|
|
|
$this->assertSame( $wikiId, $page->getWikiId() );
|
|
$this->assertSamePage( $nonexistingPage->getTitle(), $page );
|
|
}
|
|
|
|
/**
|
|
* Test that getPageForLink() maps NS_MEDIA to NS_FILE
|
|
* @covers \MediaWiki\Page\PageStore::getPageForLink
|
|
*/
|
|
public function testGetPageForLink_media() {
|
|
$link = new TitleValue( NS_MEDIA, 'Test913847659234.jpg' );
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$page = $pageStore->getPageForLink( $link );
|
|
|
|
$this->assertTrue( $page->canExist() );
|
|
$this->assertSame( NS_FILE, $page->getNamespace() );
|
|
$this->assertSame( $link->getDBkey(), $page->getDBkey() );
|
|
}
|
|
|
|
public static function provideInvalidLinks() {
|
|
yield 'section link' => [ new TitleValue( NS_MAIN, '', '#References' ) ];
|
|
yield 'special page' => [ new TitleValue( NS_SPECIAL, 'Test' ) ];
|
|
yield 'interwiki link' => [ new TitleValue( NS_MAIN, 'Test', '', 'acme' ) ];
|
|
}
|
|
|
|
/**
|
|
* Test that getPageForLink() throws InvalidArgumentException when presented with
|
|
* a link that does not refer to a proper page.
|
|
*
|
|
* @dataProvider provideInvalidLinks
|
|
* @covers \MediaWiki\Page\PageStore::getPageForLink
|
|
*/
|
|
public function testGetPageForLink_invalid( $link ) {
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$this->expectException( InvalidArgumentException::class );
|
|
$pageStore->getPageForLink( $link );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord for an existing page by name
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_existing() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
$ns = $existingPage->getNamespace();
|
|
$dbkey = $existingPage->getDBkey();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByName( $ns, $dbkey );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
|
|
$linkCache = $this->getServiceContainer()->getLinkCache();
|
|
$this->assertSame( $page->getId(), $linkCache->getGoodLinkID( $page ) );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord for an existing page by name
|
|
* with no LinkCache provided.
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_existing_noLinkCache() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
$ns = $existingPage->getNamespace();
|
|
$dbkey = $existingPage->getDBkey();
|
|
|
|
$pageStore = $this->getPageStore( [], [ 'linkCache' => null ] );
|
|
$page = $pageStore->getPageByName( $ns, $dbkey );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
|
|
$linkCache = $this->getServiceContainer()->getLinkCache();
|
|
$this->assertSame( $page->getId(), $linkCache->getGoodLinkID( $page ) );
|
|
}
|
|
|
|
/**
|
|
* Test that we get null if we look up a non-existing page by name
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_nonexisting() {
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$ns = $nonexistingPage->getNamespace();
|
|
$dbkey = $nonexistingPage->getDBkey();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByName( $ns, $dbkey );
|
|
|
|
$this->assertNull( $page );
|
|
|
|
$linkCache = $this->getServiceContainer()->getLinkCache();
|
|
$this->assertTrue( $linkCache->isBadLink( $nonexistingPage ) );
|
|
}
|
|
|
|
/**
|
|
* Test that we get null if we look up a page known to be not existing,
|
|
* without hitting the database.
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_nonexisting_cached() {
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$ns = $nonexistingPage->getNamespace();
|
|
$dbkey = $nonexistingPage->getDBkey();
|
|
|
|
$linkCache = $this->getServiceContainer()->getLinkCache();
|
|
$linkCache->addBadLinkObj( $nonexistingPage );
|
|
|
|
$mockLB = $this->createNoOpMock( LoadBalancer::class );
|
|
$pageStore = $this->getPageStore( [], [ 'doLoadBalancer' => $mockLB ] );
|
|
$page = $pageStore->getPageByName( $ns, $dbkey );
|
|
|
|
$this->assertNull( $page );
|
|
$this->assertTrue( $linkCache->isBadLink( $nonexistingPage ) );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord from a cached row
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_cachedFullRow() {
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$ns = $nonexistingPage->getNamespace();
|
|
$dbkey = $nonexistingPage->getDBkey();
|
|
|
|
$row = (object)[
|
|
'page_id' => 8,
|
|
'page_namespace' => $ns,
|
|
'page_title' => $dbkey,
|
|
'page_is_redirect' => 0,
|
|
'page_is_new' => 1,
|
|
'page_touched' => '12345',
|
|
'page_links_updated' => '12345',
|
|
'page_latest' => 118,
|
|
'page_len' => 155,
|
|
'page_content_model' => CONTENT_FORMAT_TEXT,
|
|
'page_lang' => 'xyz',
|
|
];
|
|
|
|
$linkCache = $this->getServiceContainer()->getLinkCache();
|
|
$linkCache->addGoodLinkObjFromRow( $nonexistingPage, $row );
|
|
|
|
$mockLB = $this->createNoOpMock( LoadBalancer::class );
|
|
|
|
$pageStore = $this->getPageStore( [], [ 'doLoadBalancer' => $mockLB ] );
|
|
$page = $pageStore->getPageByName( $ns, $dbkey );
|
|
|
|
$this->assertSame( $row->page_id, $page->getId() );
|
|
$this->assertSame( $row->page_namespace, $page->getNamespace() );
|
|
$this->assertSame( $row->page_title, $page->getDBkey() );
|
|
$this->assertSame( $row->page_latest, $page->getLatest() );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord when an incomplete row exists in the cache
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_cachedFakeRow() {
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$ns = $nonexistingPage->getNamespace();
|
|
$dbkey = $nonexistingPage->getDBkey();
|
|
|
|
$linkCache = $this->getServiceContainer()->getLinkCache();
|
|
$linkCache->clearLink( $nonexistingPage );
|
|
|
|
// Has all fields needed by LinkCache, but not all fields needed by PageStore.
|
|
// This may happen when legacy code injects rows directly into LinkCache.
|
|
$this->addGoodLinkObject( 8, $nonexistingPage );
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByName( $ns, $dbkey );
|
|
|
|
$this->assertSame( 8, $page->getId() );
|
|
$this->assertSame( $nonexistingPage->getNamespace(), $page->getNamespace() );
|
|
$this->assertSame( $nonexistingPage->getDBkey(), $page->getDBkey() );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::getPageByText
|
|
*/
|
|
public function testGetPageByText_existing() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByText( $existingPage->getTitle()->getPrefixedText() );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
|
|
$page = $pageStore->getExistingPageByText( $existingPage->getTitle()->getPrefixedText() );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::getPageByText
|
|
*/
|
|
public function testGetPageByText_nonexisting() {
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByText( $nonexistingPage->getTitle()->getPrefixedText() );
|
|
$this->assertFalse( $page->exists() );
|
|
$this->assertTrue( $nonexistingPage->isSamePageAs( $page ) );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::getExistingPageByText
|
|
*/
|
|
public function testGetExistingPageByText_existing() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getExistingPageByText( $existingPage->getTitle()->getPrefixedText() );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::getExistingPageByText
|
|
*/
|
|
public function testGetExistingPageByText_nonexisting() {
|
|
$nonexistingPage = $this->getNonexistingTestPage();
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getExistingPageByText( $nonexistingPage->getTitle()->getPrefixedText() );
|
|
$this->assertNull( $page );
|
|
}
|
|
|
|
/**
|
|
* Configure the load balancer to route queries for the "foreign" domain to the test DB.
|
|
*
|
|
* @param string $wikiId
|
|
*/
|
|
private function setDomainAlias( $wikiId ) {
|
|
$dbLoadBalancer = $this->getServiceContainer()->getDBLoadBalancer();
|
|
$dbLoadBalancer->setDomainAliases( [ $wikiId => $dbLoadBalancer->getLocalDomainID() ] );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord from another wiki by name
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_crossWiki() {
|
|
$wikiId = 'acme';
|
|
$this->setDomainAlias( $wikiId );
|
|
|
|
$existingPage = $this->getExistingTestPage();
|
|
$ns = $existingPage->getNamespace();
|
|
$dbkey = $existingPage->getDBkey();
|
|
|
|
$pageStore = $this->getPageStore( [], [ 'wikiId' => $wikiId, 'linkCache' => null ] );
|
|
$page = $pageStore->getPageByName( $ns, $dbkey );
|
|
|
|
$this->assertSame( $wikiId, $page->getWikiId() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
}
|
|
|
|
public static function provideGetPageByName_invalid() {
|
|
yield 'empty title' => [ NS_MAIN, '' ];
|
|
yield 'spaces in title' => [ NS_MAIN, 'Foo Bar' ];
|
|
yield 'special page' => [ NS_SPECIAL, 'Test' ];
|
|
yield 'media link' => [ NS_MEDIA, 'Test' ];
|
|
}
|
|
|
|
/**
|
|
* Test that getPageByName() throws InvalidArgumentException when presented with
|
|
* a link that does not refer to a proper page.
|
|
*
|
|
* @dataProvider provideGetPageByName_invalid
|
|
* @covers \MediaWiki\Page\PageStore::getPageByName
|
|
*/
|
|
public function testGetPageByName_invalid( $ns, $dbkey ) {
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$this->expectException( InvalidArgumentException::class );
|
|
$pageStore->getPageByName( $ns, $dbkey );
|
|
}
|
|
|
|
public static function provideInvalidTitleText() {
|
|
yield 'empty' => [ '' ];
|
|
yield 'section' => [ '#foo' ];
|
|
yield 'autoblock' => [ 'User:#12345' ];
|
|
yield 'special' => [ 'Special:RecentChanges' ];
|
|
yield 'invalid' => [ 'foo|bar' ];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideInvalidTitleText
|
|
* @covers \MediaWiki\Page\PageStore::getPageByText
|
|
*/
|
|
public function testGetPageByText_invalid( $text ) {
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByText( $text );
|
|
$this->assertNull( $page );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideInvalidTitleText
|
|
* @covers \MediaWiki\Page\PageStore::getExistingPageByText
|
|
*/
|
|
public function testGetExistingPageByText_invalid( $text ) {
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getExistingPageByText( $text );
|
|
$this->assertNull( $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord for an existing page by id
|
|
* @covers \MediaWiki\Page\PageStore::getPageById
|
|
*/
|
|
public function testGetPageById_existing() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageById( $existingPage->getId() );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we get null if we look up a non-existing page by id
|
|
* @covers \MediaWiki\Page\PageStore::getPageById
|
|
*/
|
|
public function testGetPageById_nonexisting() {
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageById( 813451092 );
|
|
|
|
$this->assertNull( $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord from another wiki by id
|
|
* @covers \MediaWiki\Page\PageStore::getPageById
|
|
*/
|
|
public function testGetPageById_crossWiki() {
|
|
$wikiId = 'acme';
|
|
$this->setDomainAlias( $wikiId );
|
|
|
|
$existingPage = $this->getExistingTestPage();
|
|
|
|
$pageStore = $this->getPageStore( [], [ 'wikiId' => $wikiId, 'linkCache' => null ] );
|
|
$page = $pageStore->getPageById( $existingPage->getId() );
|
|
|
|
$this->assertSame( $wikiId, $page->getWikiId() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we can correctly emulate the page_lang field.
|
|
* @covers \MediaWiki\Page\PageStore::getPageById
|
|
*/
|
|
public function testGetPageById_noLanguage() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageById( $existingPage->getId() );
|
|
|
|
$this->assertNull( $page->getLanguage() );
|
|
}
|
|
|
|
public static function provideGetPageById_invalid() {
|
|
yield 'zero' => [ 0 ];
|
|
yield 'negative' => [ -1 ];
|
|
}
|
|
|
|
/**
|
|
* Test that getPageById() throws InvalidArgumentException for bad IDs.
|
|
*
|
|
* @dataProvider provideGetPageById_invalid
|
|
* @covers \MediaWiki\Page\PageStore::getPageById
|
|
*/
|
|
public function testGetPageById_invalid( $id ) {
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$this->expectException( InvalidArgumentException::class );
|
|
$pageStore->getPageById( $id );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord for an existing page by id
|
|
*
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByIdentity_existing() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
$identity = $existingPage->getTitle()->toPageIdentity();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByReference( $identity );
|
|
|
|
$this->assertTrue( $page->exists() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord from cached data even if we pass in a
|
|
* PageIdentity that provides a page ID (T296063#7520023).
|
|
*
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByIdentity_cached() {
|
|
$title = $this->makeMockTitle( __METHOD__, [ 'id' => 23 ] );
|
|
$this->addGoodLinkObject( 23, $title );
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByReference( $title );
|
|
|
|
$this->assertNotNull( $page );
|
|
$this->assertSame( 23, $page->getId() );
|
|
}
|
|
|
|
/**
|
|
* Test that we get null if we look up a page with ID 0
|
|
*
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByIdentity_knowNonexisting() {
|
|
$nonexistingPage = new PageIdentityValue( 0, NS_MAIN, 'Test', PageIdentity::LOCAL );
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByReference( $nonexistingPage );
|
|
|
|
$this->assertNull( $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we get null if we look up a page with an ID that does not exist
|
|
*
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByIdentity_notFound() {
|
|
$nonexistingPage = new PageIdentityValue( 523478562, NS_MAIN, 'Test', PageIdentity::LOCAL );
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByReference( $nonexistingPage );
|
|
|
|
$this->assertNull( $page );
|
|
}
|
|
|
|
/**
|
|
* Test that getPageByIdentity() returns any ExistingPageRecord unchanged
|
|
*
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByIdentity_PageRecord() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
$rec = $existingPage->toPageRecord();
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$page = $pageStore->getPageByReference( $rec );
|
|
|
|
$this->assertSame( $rec, $page );
|
|
}
|
|
|
|
/**
|
|
* Test that we get a PageRecord from another wiki by id
|
|
*
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByIdentity_crossWiki() {
|
|
$wikiId = 'acme';
|
|
$this->setDomainAlias( $wikiId );
|
|
|
|
$existingPage = $this->getExistingTestPage();
|
|
|
|
$identity = new PageIdentityValue(
|
|
$existingPage->getId(),
|
|
$existingPage->getNamespace(),
|
|
$existingPage->getDBkey(),
|
|
$wikiId
|
|
);
|
|
|
|
$pageStore = $this->getPageStore( [], [ 'wikiId' => $wikiId, 'linkCache' => null ] );
|
|
$page = $pageStore->getPageByReference( $identity );
|
|
|
|
$this->assertSame( $wikiId, $page->getWikiId() );
|
|
$this->assertSamePage( $existingPage, $page );
|
|
}
|
|
|
|
public function provideGetPageByIdentity_invalid() {
|
|
yield 'section' => [
|
|
$this->makeMockTitle( '', [ 'fragment' => 'See also' ] ),
|
|
InvalidArgumentException::class
|
|
];
|
|
yield 'special' => [
|
|
$this->makeMockTitle( 'Blankpage', [ 'namespace' => NS_SPECIAL ] ),
|
|
InvalidArgumentException::class
|
|
];
|
|
yield 'interwiki' => [
|
|
$this->makeMockTitle( 'Foo', [ 'interwiki' => 'acme' ] ),
|
|
InvalidArgumentException::class
|
|
];
|
|
|
|
$identity = new PageIdentityValue( 7, NS_MAIN, 'Test', 'acme' );
|
|
yield 'cross-wiki' => [ $identity, PreconditionException::class ];
|
|
}
|
|
|
|
/**
|
|
* Test that getPageByIdentity() throws InvalidArgumentException for bad IDs.
|
|
*
|
|
* @dataProvider provideGetPageByIdentity_invalid
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByIdentity_invalid( $identity, $exception ) {
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$this->expectException( $exception );
|
|
$pageStore->getPageByReference( $identity );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::newPageRecordFromRow
|
|
* @covers \MediaWiki\Page\PageStore::getSelectFields
|
|
*/
|
|
public function testNewPageRecordFromRow() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$row = $this->db->newSelectQueryBuilder()
|
|
->select( $pageStore->getSelectFields() )
|
|
->from( 'page' )
|
|
->where( [ 'page_id' => $existingPage->getId() ] )
|
|
->fetchRow();
|
|
|
|
$rec = $pageStore->newPageRecordFromRow( $row );
|
|
$this->assertSamePage( $existingPage, $rec );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::newSelectQueryBuilder
|
|
*/
|
|
public function testNewSelectQueryBuilder() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
|
|
$wikiId = 'acme';
|
|
$this->setDomainAlias( $wikiId );
|
|
|
|
$pageStore = $this->getPageStore( [], [ 'wikiId' => $wikiId, 'linkCache' => null ] );
|
|
|
|
$rec = $pageStore->newSelectQueryBuilder()
|
|
->wherePageIds( $existingPage->getId() )
|
|
->fetchPageRecord();
|
|
|
|
$this->assertSame( $wikiId, $rec->getWikiId() );
|
|
$this->assertSamePage( $existingPage, $rec );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::newSelectQueryBuilder
|
|
*/
|
|
public function testNewSelectQueryBuilder_passDatabase() {
|
|
$pageStore = $this->getPageStore();
|
|
|
|
// Test that the provided DB connection is used.
|
|
$db = $this->createMock( IDatabase::class );
|
|
$db->expects( $this->atLeastOnce() )->method( 'selectRow' )->willReturn( false );
|
|
|
|
$pageStore->newSelectQueryBuilder( $db )
|
|
->fetchPageRecord();
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::newSelectQueryBuilder
|
|
*/
|
|
public function testNewSelectQueryBuilder_passFlags() {
|
|
// Test that the provided DB connection is used.
|
|
$db = $this->createMock( IDatabase::class );
|
|
$db->expects( $this->atLeastOnce() )->method( 'selectRow' )->willReturn( false );
|
|
|
|
// Test that the load balancer is asked for a master connection
|
|
$lb = $this->createMock( LoadBalancer::class );
|
|
$lb->expects( $this->atLeastOnce() )
|
|
->method( 'getConnectionRef' )
|
|
->with( DB_PRIMARY )
|
|
->willReturn( $db );
|
|
|
|
$pageStore = $this->getPageStore(
|
|
[
|
|
'LanguageCode' => 'qxx',
|
|
'PageLanguageUseDB' => true
|
|
],
|
|
[ 'dbLoadBalancer' => $lb ]
|
|
);
|
|
|
|
$pageStore->newSelectQueryBuilder( IDBAccessObject::READ_LATEST )
|
|
->fetchPageRecord();
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::getSubpages
|
|
*/
|
|
public function testGetSubpages() {
|
|
$existingPage = $this->getExistingTestPage();
|
|
$title = $existingPage->getTitle();
|
|
|
|
$this->overrideConfigValue(
|
|
MainConfigNames::NamespacesWithSubpages,
|
|
[ $title->getNamespace() => true ]
|
|
);
|
|
|
|
$existingSubpageA = $this->getExistingTestPage( $title->getSubpage( 'A' ) );
|
|
$existingSubpageB = $this->getExistingTestPage( $title->getSubpage( 'B' ) );
|
|
|
|
$notQuiteSubpageTitle = $title->getPrefixedDBkey() . 'X'; // no slash!
|
|
$this->getExistingTestPage( $notQuiteSubpageTitle );
|
|
|
|
$pageStore = $this->getPageStore();
|
|
|
|
$subpages = iterator_to_array( $pageStore->getSubpages( $title, 100 ) );
|
|
|
|
$this->assertCount( 2, $subpages );
|
|
$this->assertTrue( $existingSubpageA->isSamePageAs( $subpages[0] ) );
|
|
$this->assertTrue( $existingSubpageB->isSamePageAs( $subpages[1] ) );
|
|
|
|
// make sure the limit works as well
|
|
$this->assertCount( 1, $pageStore->getSubpages( $title, 1 ) );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Page\PageStore::getSubpages
|
|
*/
|
|
public function testGetSubpages_disabled() {
|
|
$this->overrideConfigValue( MainConfigNames::NamespacesWithSubpages, [] );
|
|
|
|
$existingPage = $this->getExistingTestPage();
|
|
$title = $existingPage->getTitle();
|
|
|
|
$this->getExistingTestPage( $title->getSubpage( 'A' ) );
|
|
$this->getExistingTestPage( $title->getSubpage( 'B' ) );
|
|
|
|
$pageStore = $this->getPageStore();
|
|
$this->assertCount( 0, $pageStore->getSubpages( $title, 100 ) );
|
|
}
|
|
|
|
/**
|
|
* See T295931. If removing TitleExists hook, remove this test.
|
|
*
|
|
* @covers \MediaWiki\Page\PageStore::getPageByReference
|
|
*/
|
|
public function testGetPageByReferenceTitleExistsHook() {
|
|
$this->setTemporaryHook( 'TitleExists', static function ( $title, &$exists ) {
|
|
$exists = true;
|
|
} );
|
|
$this->assertNull(
|
|
$this->getPageStore()->getPageByReference(
|
|
Title::newFromText( __METHOD__ )
|
|
)
|
|
);
|
|
}
|
|
|
|
}
|