Merge "Exclude redirects from Special:Fewestrevisions"
[lhc/web/wiklou.git] / tests / phpunit / includes / externalstore / ExternalStoreFactoryTest.php
1 <?php
2
3 use Wikimedia\Rdbms\LBFactory;
4
5 /**
6 * @covers ExternalStoreFactory
7 * @covers ExternalStoreAccess
8 */
9 class ExternalStoreFactoryTest extends MediaWikiTestCase {
10
11 use MediaWikiCoversValidator;
12
13 /**
14 * @expectedException ExternalStoreException
15 */
16 public function testExternalStoreFactory_noStores1() {
17 $factory = new ExternalStoreFactory( [], [], 'test-id' );
18 $factory->getStore( 'ForTesting' );
19 }
20
21 /**
22 * @expectedException ExternalStoreException
23 */
24 public function testExternalStoreFactory_noStores2() {
25 $factory = new ExternalStoreFactory( [], [], 'test-id' );
26 $factory->getStore( 'foo' );
27 }
28
29 public function provideStoreNames() {
30 yield 'Same case as construction' => [ 'ForTesting' ];
31 yield 'All lower case' => [ 'fortesting' ];
32 yield 'All upper case' => [ 'FORTESTING' ];
33 yield 'Mix of cases' => [ 'FOrTEsTInG' ];
34 }
35
36 /**
37 * @dataProvider provideStoreNames
38 */
39 public function testExternalStoreFactory_someStore_protoMatch( $proto ) {
40 $factory = new ExternalStoreFactory( [ 'ForTesting' ], [], 'test-id' );
41 $store = $factory->getStore( $proto );
42 $this->assertInstanceOf( ExternalStoreForTesting::class, $store );
43 }
44
45 /**
46 * @dataProvider provideStoreNames
47 * @expectedException ExternalStoreException
48 */
49 public function testExternalStoreFactory_someStore_noProtoMatch( $proto ) {
50 $factory = new ExternalStoreFactory( [ 'SomeOtherClassName' ], [], 'test-id' );
51 $factory->getStore( $proto );
52 }
53
54 /**
55 * @covers ExternalStoreFactory::getProtocols
56 * @covers ExternalStoreFactory::getWriteBaseUrls
57 * @covers ExternalStoreFactory::getStore
58 */
59 public function testStoreFactoryBasic() {
60 $active = [ 'memory', 'mwstore' ];
61 $defaults = [ 'memory://cluster1', 'memory://cluster2', 'mwstore://memstore1' ];
62 $esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
63 $this->setMwGlobals( 'wgFileBackends', [
64 [
65 'name' => 'memstore1',
66 'class' => 'MemoryFileBackend',
67 'domain' => 'its-all-in-your-head',
68 'readOnly' => 'reason is a lie',
69 'lockManager' => 'nullLockManager'
70 ]
71 ] );
72
73 $this->assertEquals( $active, $esFactory->getProtocols() );
74 $this->assertEquals( $defaults, $esFactory->getWriteBaseUrls() );
75
76 /** @var ExternalStoreMemory $store */
77 $store = $esFactory->getStore( 'memory' );
78 $this->assertInstanceOf( ExternalStoreMemory::class, $store );
79 $this->assertFalse( $store->isReadOnly( 'cluster1' ), "Location is writable" );
80 $this->assertFalse( $store->isReadOnly( 'cluster2' ), "Location is writable" );
81
82 $mwStore = $esFactory->getStore( 'mwstore' );
83 $this->assertTrue( $mwStore->isReadOnly( 'memstore1' ), "Location is read-only" );
84
85 $lb = $this->getMockBuilder( \Wikimedia\Rdbms\LoadBalancer::class )
86 ->disableOriginalConstructor()->getMock();
87 $lb->expects( $this->any() )->method( 'getReadOnlyReason' )->willReturn( 'Locked' );
88 $lbFactory = $this->getMockBuilder( LBFactory::class )
89 ->disableOriginalConstructor()->getMock();
90 $lbFactory->expects( $this->any() )->method( 'getExternalLB' )->willReturn( $lb );
91
92 $this->setService( 'DBLoadBalancerFactory', $lbFactory );
93
94 $active = [ 'db', 'mwstore' ];
95 $defaults = [ 'db://clusterX' ];
96 $esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
97 $this->assertEquals( $active, $esFactory->getProtocols() );
98 $this->assertEquals( $defaults, $esFactory->getWriteBaseUrls() );
99
100 $store->clear();
101 }
102
103 /**
104 * @covers ExternalStoreFactory::getStoreForUrl
105 * @covers ExternalStoreFactory::getStoreLocationFromUrl
106 */
107 public function testStoreFactoryReadWrite() {
108 $active = [ 'memory' ]; // active store types
109 $defaults = [ 'memory://cluster1', 'memory://cluster2' ];
110 $esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
111 $access = new ExternalStoreAccess( $esFactory );
112
113 /** @var ExternalStoreMemory $storeLocal */
114 $storeLocal = $esFactory->getStore( 'memory' );
115 /** @var ExternalStoreMemory $storeOther */
116 $storeOther = $esFactory->getStore( 'memory', [ 'domain' => 'other' ] );
117 $this->assertInstanceOf( ExternalStoreMemory::class, $storeLocal );
118 $this->assertInstanceOf( ExternalStoreMemory::class, $storeOther );
119
120 $v1 = wfRandomString();
121 $v2 = wfRandomString();
122 $v3 = wfRandomString();
123
124 $this->assertEquals( false, $storeLocal->fetchFromURL( 'memory://cluster1/1' ) );
125
126 $url1 = 'memory://cluster1/1';
127 $this->assertEquals(
128 $url1,
129 $esFactory->getStoreForUrl( 'memory://cluster1' )
130 ->store( $esFactory->getStoreLocationFromUrl( 'memory://cluster1' ), $v1 )
131 );
132 $this->assertEquals(
133 $v1,
134 $esFactory->getStoreForUrl( 'memory://cluster1/1' )
135 ->fetchFromURL( 'memory://cluster1/1' )
136 );
137 $this->assertEquals( $v1, $storeLocal->fetchFromURL( 'memory://cluster1/1' ) );
138
139 $url2 = $access->insert( $v2 );
140 $url3 = $access->insert( $v3, [ 'domain' => 'other' ] );
141 $this->assertNotFalse( $url2 );
142 $this->assertNotFalse( $url3 );
143 // There is only one active store type
144 $this->assertEquals( $v2, $storeLocal->fetchFromURL( $url2 ) );
145 $this->assertEquals( $v3, $storeOther->fetchFromURL( $url3 ) );
146 $this->assertEquals( false, $storeOther->fetchFromURL( $url2 ) );
147 $this->assertEquals( false, $storeLocal->fetchFromURL( $url3 ) );
148
149 $res = $access->fetchFromURLs( [ $url1, $url2, $url3 ] );
150 $this->assertEquals( [ $url1 => $v1, $url2 => $v2, $url3 => false ], $res, "Local-only" );
151
152 $storeLocal->clear();
153 $storeOther->clear();
154 }
155 }