We'd lose more useful coverage and spend more effort keeping these accurate through refactors etc than is worth the theoretically "bad" accidental coverage. Plus, even then we still very often forget to update these and waste time digging into seemingly uncovered code and either write duplicate test or discover they are already covered. Especially for private methods this can be a flawed endavour as we tend to trust tests when changing those and thus feel we shouldn't update tests, except it's riddled with private method mentions for coverage purposes (not in terms of actual called code). I think the best practice is to write good narrow unit tests, not to write bad tests and then hide their coverage. Generally that's what we do. Maybe these are useful when invoking a huge legacy class, but generally we don't need these I think, at least in the libs our team maintains. Change-Id: I4c7d826c7ec654b9efa20778c46c498784661f1c
159 lines
5.5 KiB
PHP
159 lines
5.5 KiB
PHP
<?php
|
|
|
|
use Wikimedia\LightweightObjectStore\StorageAwareness;
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @covers ReplicatedBagOStuff
|
|
*/
|
|
class ReplicatedBagOStuffTest extends \MediaWikiUnitTestCase {
|
|
/** @var HashBagOStuff */
|
|
private $writeCache;
|
|
/** @var HashBagOStuff */
|
|
private $readCache;
|
|
/** @var ReplicatedBagOStuff */
|
|
private $cache;
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
|
|
$this->writeCache = new HashBagOStuff();
|
|
$this->readCache = new HashBagOStuff();
|
|
$this->cache = new ReplicatedBagOStuff( [
|
|
'keyspace' => 'repl_local',
|
|
'writeFactory' => $this->writeCache,
|
|
'readFactory' => $this->readCache,
|
|
] );
|
|
}
|
|
|
|
public function testSet() {
|
|
$key = $this->cache->makeKey( 'a', 'key' );
|
|
$value = 'a value';
|
|
|
|
$this->cache->set( $key, $value );
|
|
|
|
$this->assertSame( $value, $this->writeCache->get( $key ), 'Written' );
|
|
$this->assertFalse( $this->readCache->get( $key ), 'Async replication' );
|
|
}
|
|
|
|
public function testGet() {
|
|
$key = $this->cache->makeKey( 'a', 'key' );
|
|
|
|
$write = 'new value';
|
|
$this->writeCache->set( $key, $write );
|
|
$read = 'old value';
|
|
$this->readCache->set( $key, $read );
|
|
|
|
$this->assertSame( $read, $this->cache->get( $key ), 'Async replication' );
|
|
}
|
|
|
|
public function testGetAbsent() {
|
|
$key = $this->cache->makeKey( 'a', 'key' );
|
|
$value = 'a value';
|
|
$this->writeCache->set( $key, $value );
|
|
|
|
$this->assertFalse( $this->cache->get( $key ), 'Async replication' );
|
|
}
|
|
|
|
public function testGetSetMulti() {
|
|
$keyA = $this->cache->makeKey( 'key', 'a' );
|
|
$keyB = $this->cache->makeKey( 'key', 'b' );
|
|
$valueAOld = 'one old value';
|
|
$valueBOld = 'another old value';
|
|
$valueANew = 'one new value';
|
|
$valueBNew = 'another new value';
|
|
|
|
$this->writeCache->setMulti( [ $keyA => $valueANew, $keyB => $valueBNew ] );
|
|
$this->readCache->setMulti( [ $keyA => $valueAOld, $keyB => $valueBOld ] );
|
|
|
|
$this->assertEquals(
|
|
[ $keyA => $valueAOld, $keyB => $valueBOld ],
|
|
$this->cache->getMulti( [ $keyA, $keyB ] ),
|
|
'Async replication'
|
|
);
|
|
}
|
|
|
|
public function testGetSetRaw() {
|
|
$key = 'a:key';
|
|
$value = 'a value';
|
|
$this->cache->set( $key, $value );
|
|
|
|
// Write to master.
|
|
$this->assertEquals( $value, $this->writeCache->get( $key ) );
|
|
// Don't write to replica. Replication is deferred to backend.
|
|
$this->assertFalse( $this->readCache->get( $key ) );
|
|
}
|
|
|
|
public function testErrorHandling() {
|
|
$wCache = $this->createPartialMock( HashBagOStuff::class, [ 'set' ] );
|
|
$wCacheWrapper = TestingAccessWrapper::newFromObject( $wCache );
|
|
$wCacheNextError = StorageAwareness::ERR_NONE;
|
|
$wCache->method( 'set' )
|
|
->willReturnCallback( static function () use ( $wCacheWrapper, &$wCacheNextError ) {
|
|
if ( $wCacheNextError !== StorageAwareness::ERR_NONE ) {
|
|
$wCacheWrapper->setLastError( $wCacheNextError );
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} );
|
|
$rCache = $this->createPartialMock( HashBagOStuff::class, [ 'get' ] );
|
|
$rCacheWrapper = TestingAccessWrapper::newFromObject( $rCache );
|
|
$rCacheNextError = StorageAwareness::ERR_NONE;
|
|
$rCache->method( 'get' )
|
|
->willReturnCallback( static function () use ( $rCacheWrapper, &$rCacheNextError ) {
|
|
if ( $rCacheNextError !== StorageAwareness::ERR_NONE ) {
|
|
$rCacheWrapper->setLastError( $rCacheNextError );
|
|
}
|
|
|
|
return false;
|
|
} );
|
|
$cache = new ReplicatedBagOStuff( [
|
|
'keyspace' => 'repl_local',
|
|
'writeFactory' => $wCache,
|
|
'readFactory' => $rCache,
|
|
] );
|
|
$cacheWrapper = TestingAccessWrapper::newFromObject( $cache );
|
|
$key1 = 'a:key';
|
|
|
|
$wp1 = $cache->watchErrors();
|
|
$cache->get( $key1 );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $rCache->getLastError() );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $cache->getLastError() );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $cache->getLastError( $wp1 ) );
|
|
|
|
$cache->set( $key1, 'value', 3600 );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $wCache->getLastError() );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $cache->getLastError() );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $cache->getLastError( $wp1 ) );
|
|
|
|
// Use a different key to avoid the "sessionConsistencyWindow" configuration
|
|
$key2 = 'b:key';
|
|
$wCacheNextError = StorageAwareness::ERR_NO_RESPONSE;
|
|
$rCacheNextError = StorageAwareness::ERR_UNREACHABLE;
|
|
|
|
$cache->get( $key2 );
|
|
$this->assertSame( $rCacheNextError, $rCache->getLastError() );
|
|
$this->assertSame( $rCacheNextError, $cache->getLastError() );
|
|
|
|
$wp2 = $cache->watchErrors();
|
|
$cache->set( $key2, 'value', 3600 );
|
|
$wp3 = $cache->watchErrors();
|
|
$this->assertSame( $wCacheNextError, $wCache->getLastError() );
|
|
$this->assertSame( $wCacheNextError, $cache->getLastError() );
|
|
$this->assertSame( $wCacheNextError, $cache->getLastError( $wp1 ) );
|
|
$this->assertSame( $wCacheNextError, $cache->getLastError( $wp2 ) );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $cache->getLastError( $wp3 ) );
|
|
|
|
$cacheWrapper->setLastError( StorageAwareness::ERR_UNEXPECTED );
|
|
$wp4 = $cache->watchErrors();
|
|
$this->assertSame( StorageAwareness::ERR_UNEXPECTED, $cache->getLastError() );
|
|
$this->assertSame( StorageAwareness::ERR_UNEXPECTED, $cache->getLastError( $wp1 ) );
|
|
$this->assertSame( StorageAwareness::ERR_UNEXPECTED, $cache->getLastError( $wp2 ) );
|
|
$this->assertSame( StorageAwareness::ERR_UNEXPECTED, $cache->getLastError( $wp3 ) );
|
|
$this->assertSame( StorageAwareness::ERR_NONE, $cache->getLastError( $wp4 ) );
|
|
$this->assertSame( $wCacheNextError, $wCache->getLastError() );
|
|
$this->assertSame( $rCacheNextError, $rCache->getLastError() );
|
|
}
|
|
}
|