1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
<?php
use MediaWiki\MainConfigNames;
use Wikimedia\FileBackend\MemoryFileBackend;
use Wikimedia\Rdbms\LBFactory;
use Wikimedia\Rdbms\LoadBalancer;
/**
* @covers \ExternalStoreFactory
* @covers \ExternalStoreAccess
*/
class ExternalStoreFactoryTest extends MediaWikiIntegrationTestCase {
public function testExternalStoreFactory_noStores1() {
$factory = new ExternalStoreFactory( [], [], 'test-id' );
$this->expectException( ExternalStoreException::class );
$factory->getStore( 'ForTesting' );
}
public function testExternalStoreFactory_noStores2() {
$factory = new ExternalStoreFactory( [], [], 'test-id' );
$this->expectException( ExternalStoreException::class );
$factory->getStore( 'foo' );
}
public static 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
*/
public function testExternalStoreFactory_someStore_noProtoMatch( $proto ) {
$factory = new ExternalStoreFactory( [ 'SomeOtherClassName' ], [], 'test-id' );
$this->expectException( ExternalStoreException::class );
$factory->getStore( $proto );
}
/**
* @covers \ExternalStoreFactory::getProtocols
* @covers \ExternalStoreFactory::getWriteBaseUrls
* @covers \ExternalStoreFactory::getStore
*/
public function testStoreFactoryBasic() {
$active = [ 'memory', 'mwstore' ];
$defaults = [ 'memory://cluster1', 'memory://cluster2', 'mwstore://memstore1' ];
$esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
$this->overrideConfigValue( MainConfigNames::FileBackends, [
[
'name' => 'memstore1',
'class' => MemoryFileBackend::class,
'domain' => 'its-all-in-your-head',
'readOnly' => 'reason is a lie',
'lockManager' => 'nullLockManager'
]
] );
$this->assertEquals( $active, $esFactory->getProtocols() );
$this->assertEquals( $defaults, $esFactory->getWriteBaseUrls() );
/** @var ExternalStoreMemory $store */
$store = $esFactory->getStore( 'memory' );
$this->assertInstanceOf( ExternalStoreMemory::class, $store );
$this->assertFalse( $store->isReadOnly( 'cluster1' ), "Location is writable" );
$this->assertFalse( $store->isReadOnly( 'cluster2' ), "Location is writable" );
$mwStore = $esFactory->getStore( 'mwstore' );
$this->assertTrue( $mwStore->isReadOnly( 'memstore1' ), "Location is read-only" );
$lb = $this->createMock( LoadBalancer::class );
$lb->method( 'getReadOnlyReason' )->willReturn( 'Locked' );
$lbFactory = $this->createMock( LBFactory::class );
$lbFactory->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->assertFalse( $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->assertFalse( $storeOther->fetchFromURL( $url2 ) );
$this->assertFalse( $storeLocal->fetchFromURL( $url3 ) );
$res = $access->fetchFromURLs( [ $url1, $url2, $url3 ] );
$this->assertEquals( [ $url1 => $v1, $url2 => $v2, $url3 => false ], $res, "Local-only" );
$storeLocal->clear();
$storeOther->clear();
}
}
|