* Inject dependencies from the constructor and use those instead. * Make getSystemInfo() static since its sole purpose is to collect global state. This also means it can be safely called from the Installer without needing to construct this class and its unsafe dependencies as the Installer was doing previously. * Remove private 'id' member. This cached the fetchOrInsertId() return value but was never used. * Remove selectField() fallback IGNORE-mode insert from fetchOrInsertId(). This existed to handle concurrent writes, but this isn't possible since all this runs inside a DB_MASTER lock. * Add missing call to $cache->makeKey(). Avoid passing unformatted strings directly to BagOStuff methods. Bug: T270540 Change-Id: I984526020ea4d5f0fd6f72b6be527edaf6ab0917
194 lines
6.3 KiB
PHP
194 lines
6.3 KiB
PHP
<?php
|
|
|
|
use MediaWiki\Http\HttpRequestFactory;
|
|
use Psr\Log\NullLogger;
|
|
use Wikimedia\Rdbms\IDatabase;
|
|
use Wikimedia\Rdbms\ILoadBalancer;
|
|
use Wikimedia\Timestamp\ConvertibleTimestamp;
|
|
|
|
/**
|
|
* @covers Pingback
|
|
*/
|
|
class PingbackTest extends MediaWikiUnitTestCase {
|
|
|
|
public function setUp() : void {
|
|
parent::setUp();
|
|
ConvertibleTimestamp::setFakeTime( '20110401090000' );
|
|
}
|
|
|
|
public function tearDown() : void {
|
|
ConvertibleTimestamp::setFakeTime( false );
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function testDisabled() {
|
|
$db = $this->createMock( IDatabase::class );
|
|
$lb = $this->createMock( ILoadBalancer::class );
|
|
$lb->method( 'getConnectionRef' )->willReturn( $db );
|
|
$http = $this->createMock( HttpRequestFactory::class );
|
|
|
|
// Scenario: Disabled
|
|
$config = new HashConfig( [ 'Pingback' => false ] );
|
|
$cache = new HashBagOStuff();
|
|
|
|
// Expect:
|
|
// - no db calls (no select, lock, or upsert)
|
|
// - no HTTP request
|
|
$db->expects( $this->never() )->method( $this->anything() );
|
|
$http->expects( $this->never() )->method( $this->anything() );
|
|
|
|
$pingback = new MockPingback( $config, $lb, $cache, $http, new NullLogger );
|
|
$this->assertNull( $pingback->run() );
|
|
}
|
|
|
|
public function testCacheBusy() {
|
|
$db = $this->createMock( IDatabase::class );
|
|
$lb = $this->createMock( ILoadBalancer::class );
|
|
$lb->method( 'getConnectionRef' )->willReturn( $db );
|
|
$http = $this->createMock( HttpRequestFactory::class );
|
|
|
|
// Scenario:
|
|
// - enabled
|
|
// - table is empty
|
|
// - cache lock is unavailable
|
|
$config = new HashConfig( [ 'Pingback' => true ] );
|
|
$db->expects( $this->once() )->method( 'selectField' )->willReturn( false );
|
|
$cache = $this->createMock( BagOStuff::class );
|
|
$cache->method( 'add' )->willReturn( false );
|
|
|
|
// Expect:
|
|
// - no db lock
|
|
// - no HTTP request
|
|
// - no db upsert for timestamp
|
|
$db->expects( $this->never() )->method( 'lock' );
|
|
$http->expects( $this->never() )->method( $this->anything() );
|
|
$db->expects( $this->never() )->method( 'upsert' );
|
|
|
|
$pingback = new MockPingback( $config, $lb, $cache, $http, new NullLogger );
|
|
$this->assertNull( $pingback->run() );
|
|
}
|
|
|
|
public function testDbBusy() {
|
|
$db = $this->createMock( IDatabase::class );
|
|
$lb = $this->createMock( ILoadBalancer::class );
|
|
$lb->method( 'getConnectionRef' )->willReturn( $db );
|
|
$http = $this->createMock( HttpRequestFactory::class );
|
|
|
|
// Scenario:
|
|
// - enabled
|
|
// - table is empty
|
|
// - cache lock is available
|
|
// - db lock is unavailable
|
|
$config = new HashConfig( [ 'Pingback' => true ] );
|
|
$db->expects( $this->once() )->method( 'selectField' )->willReturn( false );
|
|
$cache = new HashBagOStuff();
|
|
$db->expects( $this->once() )->method( 'lock' )->willReturn( false );
|
|
|
|
// Expect:
|
|
// - no HTTP request
|
|
// - no db upsert for timestamp
|
|
$http->expects( $this->never() )->method( $this->anything() );
|
|
$db->expects( $this->never() )->method( 'upsert' );
|
|
|
|
$pingback = new MockPingback( $config, $lb, $cache, $http, new NullLogger );
|
|
$this->assertNull( $pingback->run() );
|
|
}
|
|
|
|
public function testFirstPing() {
|
|
$db = $this->createMock( IDatabase::class );
|
|
$lb = $this->createMock( ILoadBalancer::class );
|
|
$lb->method( 'getConnectionRef' )->willReturn( $db );
|
|
$http = $this->createMock( HttpRequestFactory::class );
|
|
|
|
// Scenario:
|
|
// - enabled
|
|
// - table is empty
|
|
// - cache lock and db lock are available
|
|
$config = new HashConfig( [ 'Pingback' => true ] );
|
|
$db->expects( $this->once() )->method( 'selectField' )->willReturn( false );
|
|
$cache = new HashBagOStuff();
|
|
|
|
// Expect:
|
|
// - db lock acquired
|
|
// - HTTP POST request
|
|
// - db upsert for timestamp
|
|
$db->expects( $this->once() )->method( 'lock' )->willReturn( true );
|
|
$http->expects( $this->never() )->method( $this->anythingBut( 'post' ) );
|
|
$http->expects( $this->once() )->method( 'post' )
|
|
->with( $this->identicalTo(
|
|
'https://www.mediawiki.org/beacon/event?%7B%22some%22%3A%22stuff%22%7D;'
|
|
) )
|
|
->willReturn( true );
|
|
$db->expects( $this->once() )->method( 'upsert' );
|
|
|
|
$pingback = new MockPingback( $config, $lb, $cache, $http, new NullLogger );
|
|
$this->assertNull( $pingback->run() );
|
|
}
|
|
|
|
public function testAfterRecentPing() {
|
|
$db = $this->createMock( IDatabase::class );
|
|
$lb = $this->createMock( ILoadBalancer::class );
|
|
$lb->method( 'getConnectionRef' )->willReturn( $db );
|
|
$http = $this->createMock( HttpRequestFactory::class );
|
|
|
|
// Scenario:
|
|
// - enabled
|
|
// - table contains a ping from one hour ago
|
|
// - cache lock and db lock are available
|
|
$config = new HashConfig( [ 'Pingback' => true ] );
|
|
$db->expects( $this->once() )->method( 'selectField' )->willReturn(
|
|
ConvertibleTimestamp::convert( TS_UNIX, '20110401080000' )
|
|
);
|
|
$cache = new HashBagOStuff();
|
|
|
|
// Expect:
|
|
// - no db lock
|
|
// - no HTTP request
|
|
// - no db upsert for timestamp
|
|
$db->expects( $this->never() )->method( 'lock' );
|
|
$http->expects( $this->never() )->method( $this->anything() );
|
|
$db->expects( $this->never() )->method( 'upsert' );
|
|
|
|
$pingback = new MockPingback( $config, $lb, $cache, $http, new NullLogger );
|
|
$this->assertNull( $pingback->run() );
|
|
}
|
|
|
|
public function testAfterOldPing() {
|
|
$db = $this->createMock( IDatabase::class );
|
|
$lb = $this->createMock( ILoadBalancer::class );
|
|
$lb->method( 'getConnectionRef' )->willReturn( $db );
|
|
$http = $this->createMock( HttpRequestFactory::class );
|
|
|
|
// Scenario:
|
|
// - enabled
|
|
// - table contains a ping from over a month ago
|
|
// - cache lock and db lock are available
|
|
$config = new HashConfig( [ 'Pingback' => true ] );
|
|
$db->expects( $this->once() )->method( 'selectField' )->willReturn(
|
|
ConvertibleTimestamp::convert( TS_UNIX, '20110301080000' )
|
|
);
|
|
$cache = new HashBagOStuff();
|
|
|
|
// Expect:
|
|
// - db lock acquire
|
|
// - HTTP POST request
|
|
// - db upsert for timestamp
|
|
$db->expects( $this->once() )->method( 'lock' )->willReturn( true );
|
|
$http->expects( $this->never() )->method( $this->anythingBut( 'post' ) );
|
|
$http->expects( $this->once() )->method( 'post' )
|
|
->with( $this->identicalTo(
|
|
'https://www.mediawiki.org/beacon/event?%7B%22some%22%3A%22stuff%22%7D;'
|
|
) )
|
|
->willReturn( true );
|
|
$db->expects( $this->once() )->method( 'upsert' );
|
|
|
|
$pingback = new MockPingback( $config, $lb, $cache, $http, new NullLogger );
|
|
$this->assertNull( $pingback->run() );
|
|
}
|
|
}
|
|
|
|
class MockPingback extends Pingback {
|
|
protected function getData() : array {
|
|
return [ 'some' => 'stuff' ];
|
|
}
|
|
}
|