1<?php
2
3use PHPUnit\Framework\MockObject\MockObject;
4
5/**
6 * @covers ExternalStoreAccess
7 */
8class ExternalStoreAccessTest extends MediaWikiIntegrationTestCase {
9
10	use MediaWikiCoversValidator;
11
12	public function testBasic() {
13		$active = [ 'memory' ];
14		$defaults = [ 'memory://cluster1', 'memory://cluster2' ];
15		$esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
16		$access = new ExternalStoreAccess( $esFactory );
17
18		$this->assertFalse( $access->isReadOnly() );
19
20		/** @var ExternalStoreMemory $store */
21		$store = $esFactory->getStore( 'memory' );
22		$this->assertInstanceOf( ExternalStoreMemory::class, $store );
23
24		$location = $esFactory->getStoreLocationFromUrl( 'memory://cluster1' );
25		$this->assertFalse( $store->isReadOnly( $location ) );
26	}
27
28	/**
29	 * @covers ExternalStoreAccess::isReadOnly
30	 */
31	public function testReadOnly() {
32		/** @var  ExternalStoreMedium|MockObject $medium */
33		$medium = $this->getMockBuilder( ExternalStoreMedium::class )
34			->disableOriginalConstructor()
35			->getMock();
36
37		$medium->method( 'isReadOnly' )->willReturn( true );
38
39		/** @var  ExternalStoreFactory|MockObject $esFactory */
40		$esFactory = $this->getMockBuilder( ExternalStoreFactory::class )
41			->disableOriginalConstructor()
42			->getMock();
43
44		$esFactory->method( 'getWriteBaseUrls' )->willReturn( [ 'test:' ] );
45		$esFactory->method( 'getStoreForUrl' )->willReturn( $medium );
46		$esFactory->method( 'getStoreLocationFromUrl' )->willReturn( 'dummy' );
47
48		$access = new ExternalStoreAccess( $esFactory );
49		$this->assertTrue( $access->isReadOnly() );
50
51		$this->expectExceptionObject( new ReadOnlyError() );
52		$access->insert( 'Lorem Ipsum' );
53	}
54
55	/**
56	 * @covers ExternalStoreAccess::fetchFromURL
57	 * @covers ExternalStoreAccess::fetchFromURLs
58	 * @covers ExternalStoreAccess::insert
59	 */
60	public function testReadWrite() {
61		$active = [ 'memory' ]; // active store types
62		$defaults = [ 'memory://cluster1', 'memory://cluster2' ];
63		$esFactory = new ExternalStoreFactory( $active, $defaults, 'db-prefix' );
64		$access = new ExternalStoreAccess( $esFactory );
65
66		/** @var ExternalStoreMemory $storeLocal */
67		$storeLocal = $esFactory->getStore( 'memory' );
68		/** @var ExternalStoreMemory $storeOther */
69		$storeOther = $esFactory->getStore( 'memory', [ 'domain' => 'other' ] );
70		$this->assertInstanceOf( ExternalStoreMemory::class, $storeLocal );
71		$this->assertInstanceOf( ExternalStoreMemory::class, $storeOther );
72
73		$v1 = wfRandomString();
74		$v2 = wfRandomString();
75		$v3 = wfRandomString();
76
77		$this->assertFalse( $storeLocal->fetchFromURL( 'memory://cluster1/1' ) );
78
79		$url1 = 'memory://cluster1/1';
80		$this->assertEquals(
81			$url1,
82			$esFactory->getStoreForUrl( 'memory://cluster1' )
83				->store( $esFactory->getStoreLocationFromUrl( 'memory://cluster1' ), $v1 )
84		);
85		$this->assertEquals(
86			$v1,
87			$esFactory->getStoreForUrl( 'memory://cluster1/1' )
88				->fetchFromURL( 'memory://cluster1/1' )
89		);
90		$this->assertEquals( $v1, $storeLocal->fetchFromURL( 'memory://cluster1/1' ) );
91
92		$url2 = $access->insert( $v2 );
93		$url3 = $access->insert( $v3, [ 'domain' => 'other' ] );
94		$this->assertNotFalse( $url2 );
95		$this->assertNotFalse( $url3 );
96		// There is only one active store type
97		$this->assertEquals( $v2, $storeLocal->fetchFromURL( $url2 ) );
98		$this->assertEquals( $v3, $storeOther->fetchFromURL( $url3 ) );
99		$this->assertFalse( $storeOther->fetchFromURL( $url2 ) );
100		$this->assertFalse( $storeLocal->fetchFromURL( $url3 ) );
101
102		$res = $access->fetchFromURLs( [ $url1, $url2, $url3 ] );
103		$this->assertEquals( [ $url1 => $v1, $url2 => $v2, $url3 => false ], $res, "Local-only" );
104
105		$storeLocal->clear();
106		$storeOther->clear();
107	}
108}
109