* Inject settings and global instances as dependencies to the ExternalStoreMedium instances. This includes the local wiki domain, so that wfWikiId() calls are not scattered around. * Create ExternalStoreAccess service for read/write logic. * Deprecate the ExternalStore wrapper methods. * Add some exception cases for bogus store URLs are used instead of just giving PHP warnings and failing later. * Make moveToExternal.php require the type/protocol to decide which ExternalStoreMedium to use instead of assuming "DB". * Convert logging calls to use LoggerInterface. Change-Id: I40c3b5534fc8a31116c4c5eb64ee6e4903a6197a
142 lines
4.9 KiB
PHP
142 lines
4.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @covers ExternalStoreFactory
|
|
* @covers ExternalStoreAccess
|
|
*/
|
|
class ExternalStoreFactoryTest extends MediaWikiTestCase {
|
|
|
|
use MediaWikiCoversValidator;
|
|
|
|
/**
|
|
* @expectedException ExternalStoreException
|
|
*/
|
|
public function testExternalStoreFactory_noStores1() {
|
|
$factory = new ExternalStoreFactory( [], [], 'test-id' );
|
|
$factory->getStore( 'ForTesting' );
|
|
}
|
|
|
|
/**
|
|
* @expectedException ExternalStoreException
|
|
*/
|
|
public function testExternalStoreFactory_noStores2() {
|
|
$factory = new ExternalStoreFactory( [], [], 'test-id' );
|
|
$factory->getStore( 'foo' );
|
|
}
|
|
|
|
public function provideStoreNames() {
|
|
yield 'Same case as construction' => [ 'ForTesting' ];
|
|
yield 'All lower case' => [ 'fortesting' ];
|
|
yield 'All upper case' => [ 'FORTESTING' ];
|
|
yield 'Mix of cases' => [ 'FOrTEsTInG' ];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideStoreNames
|
|
*/
|
|
public function testExternalStoreFactory_someStore_protoMatch( $proto ) {
|
|
$factory = new ExternalStoreFactory( [ 'ForTesting' ], [], 'test-id' );
|
|
$store = $factory->getStore( $proto );
|
|
$this->assertInstanceOf( ExternalStoreForTesting::class, $store );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideStoreNames
|
|
* @expectedException ExternalStoreException
|
|
*/
|
|
public function testExternalStoreFactory_someStore_noProtoMatch( $proto ) {
|
|
$factory = new ExternalStoreFactory( [ 'SomeOtherClassName' ], [], 'test-id' );
|
|
$factory->getStore( $proto );
|
|
}
|
|
|
|
/**
|
|
* @covers ExternalStoreFactory::getProtocols
|
|
* @covers ExternalStoreFactory::getWriteBaseUrls
|
|
* @covers ExternalStoreFactory::getStore
|
|
*/
|
|
public function testStoreFactoryBasic() {
|
|
$active = [ 'memory' ];
|
|
$defaults = [ 'memory://cluster1', 'memory://cluster2' ];
|
|
$esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
|
|
|
|
$this->assertEquals( $active, $esFactory->getProtocols() );
|
|
$this->assertEquals( $defaults, $esFactory->getWriteBaseUrls() );
|
|
|
|
/** @var ExternalStoreMemory $store */
|
|
$store = $esFactory->getStore( 'memory' );
|
|
$this->assertInstanceOf( ExternalStoreMemory::class, $store );
|
|
$this->assertEquals( false, $store->isReadOnly( 'cluster1' ) );
|
|
$this->assertEquals( false, $store->isReadOnly( 'cluster2' ) );
|
|
$this->assertEquals( true, $store->isReadOnly( 'clusterOld' ) );
|
|
|
|
$lb = $this->getMockBuilder( \Wikimedia\Rdbms\LoadBalancer::class )
|
|
->disableOriginalConstructor()->getMock();
|
|
$lb->expects( $this->any() )->method( 'getReadOnlyReason' )->willReturn( 'Locked' );
|
|
$lbFactory = $this->getMockBuilder( \Wikimedia\Rdbms\LBFactory::class )
|
|
->disableOriginalConstructor()->getMock();
|
|
$lbFactory->expects( $this->any() )->method( 'getExternalLB' )->willReturn( $lb );
|
|
|
|
$this->setService( 'DBLoadBalancerFactory', $lbFactory );
|
|
|
|
$active = [ 'db', 'mwstore' ];
|
|
$defaults = [ 'db://clusterX' ];
|
|
$esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
|
|
$this->assertEquals( $active, $esFactory->getProtocols() );
|
|
$this->assertEquals( $defaults, $esFactory->getWriteBaseUrls() );
|
|
|
|
$store->clear();
|
|
}
|
|
|
|
/**
|
|
* @covers ExternalStoreFactory::getStoreForUrl
|
|
* @covers ExternalStoreFactory::getStoreLocationFromUrl
|
|
*/
|
|
public function testStoreFactoryReadWrite() {
|
|
$active = [ 'memory' ]; // active store types
|
|
$defaults = [ 'memory://cluster1', 'memory://cluster2' ];
|
|
$esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
|
|
$access = new ExternalStoreAccess( $esFactory );
|
|
|
|
/** @var ExternalStoreMemory $storeLocal */
|
|
$storeLocal = $esFactory->getStore( 'memory' );
|
|
/** @var ExternalStoreMemory $storeOther */
|
|
$storeOther = $esFactory->getStore( 'memory', [ 'domain' => 'other' ] );
|
|
$this->assertInstanceOf( ExternalStoreMemory::class, $storeLocal );
|
|
$this->assertInstanceOf( ExternalStoreMemory::class, $storeOther );
|
|
|
|
$v1 = wfRandomString();
|
|
$v2 = wfRandomString();
|
|
$v3 = wfRandomString();
|
|
|
|
$this->assertEquals( false, $storeLocal->fetchFromURL( 'memory://cluster1/1' ) );
|
|
|
|
$url1 = 'memory://cluster1/1';
|
|
$this->assertEquals(
|
|
$url1,
|
|
$esFactory->getStoreForUrl( 'memory://cluster1' )
|
|
->store( $esFactory->getStoreLocationFromUrl( 'memory://cluster1' ), $v1 )
|
|
);
|
|
$this->assertEquals(
|
|
$v1,
|
|
$esFactory->getStoreForUrl( 'memory://cluster1/1' )
|
|
->fetchFromURL( 'memory://cluster1/1' )
|
|
);
|
|
$this->assertEquals( $v1, $storeLocal->fetchFromURL( 'memory://cluster1/1' ) );
|
|
|
|
$url2 = $access->insert( $v2 );
|
|
$url3 = $access->insert( $v3, [ 'domain' => 'other' ] );
|
|
$this->assertNotFalse( $url2 );
|
|
$this->assertNotFalse( $url3 );
|
|
// There is only one active store type
|
|
$this->assertEquals( $v2, $storeLocal->fetchFromURL( $url2 ) );
|
|
$this->assertEquals( $v3, $storeOther->fetchFromURL( $url3 ) );
|
|
$this->assertEquals( false, $storeOther->fetchFromURL( $url2 ) );
|
|
$this->assertEquals( false, $storeLocal->fetchFromURL( $url3 ) );
|
|
|
|
$res = $access->fetchFromURLs( [ $url1, $url2, $url3 ] );
|
|
$this->assertEquals( [ $url1 => $v1, $url2 => $v2, $url3 => false ], $res, "Local-only" );
|
|
|
|
$storeLocal->clear();
|
|
$storeOther->clear();
|
|
}
|
|
}
|