X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=tests%2Fphpunit%2Fincludes%2Flibs%2Frdbms%2Fdatabase%2FDatabaseSQLTest.php;h=3756da26136bccdd61d913d355f8112cf0bff759;hp=40e07d8f3cf1201039a00ca198c622cea29604fb;hb=54d50ef3921dc9f30dc4d863ddc471dc564998e9;hpb=882323309ff1e3ee45e1e89ee8e57484f54f6e32 diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php index 40e07d8f3c..3756da2613 100644 --- a/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php +++ b/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php @@ -6,6 +6,7 @@ use Wikimedia\Rdbms\Database; use Wikimedia\TestingAccessWrapper; use Wikimedia\Rdbms\DBTransactionStateError; use Wikimedia\Rdbms\DBUnexpectedError; +use Wikimedia\Rdbms\DBTransactionError; /** * Test the parts of the Database abstract class that deal @@ -14,6 +15,7 @@ use Wikimedia\Rdbms\DBUnexpectedError; class DatabaseSQLTest extends PHPUnit\Framework\TestCase { use MediaWikiCoversValidator; + use PHPUnit4And6Compat; /** @var DatabaseTestHelper|Database */ private $database; @@ -1401,22 +1403,33 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { // phpcs:ignore Generic.Files.LineLength $this->assertLastSql( 'BEGIN; SAVEPOINT wikimedia_rdbms_atomic1; ROLLBACK TO SAVEPOINT wikimedia_rdbms_atomic1; COMMIT' ); - $this->database->doAtomicSection( __METHOD__, function () { - } ); + $noOpCallack = function () { + }; + + $this->database->doAtomicSection( __METHOD__, $noOpCallack, IDatabase::ATOMIC_CANCELABLE ); + $this->assertLastSql( 'BEGIN; COMMIT' ); + + $this->database->doAtomicSection( __METHOD__, $noOpCallack ); $this->assertLastSql( 'BEGIN; COMMIT' ); $this->database->begin( __METHOD__ ); - $this->database->doAtomicSection( __METHOD__, function () { - } ); + $this->database->doAtomicSection( __METHOD__, $noOpCallack, IDatabase::ATOMIC_CANCELABLE ); $this->database->rollback( __METHOD__ ); // phpcs:ignore Generic.Files.LineLength $this->assertLastSql( 'BEGIN; SAVEPOINT wikimedia_rdbms_atomic1; RELEASE SAVEPOINT wikimedia_rdbms_atomic1; ROLLBACK' ); $this->database->begin( __METHOD__ ); try { - $this->database->doAtomicSection( __METHOD__, function () { - throw new RuntimeException( 'Test exception' ); - } ); + $this->database->doAtomicSection( + __METHOD__, + function () { + $this->database->startAtomic( 'inner_func1' ); + $this->database->startAtomic( 'inner_func2' ); + + throw new RuntimeException( 'Test exception' ); + }, + IDatabase::ATOMIC_CANCELABLE + ); $this->fail( 'Expected exception not thrown' ); } catch ( RuntimeException $ex ) { $this->assertSame( 'Test exception', $ex->getMessage() ); @@ -1424,6 +1437,21 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { $this->database->commit( __METHOD__ ); // phpcs:ignore Generic.Files.LineLength $this->assertLastSql( 'BEGIN; SAVEPOINT wikimedia_rdbms_atomic1; ROLLBACK TO SAVEPOINT wikimedia_rdbms_atomic1; COMMIT' ); + + $this->database->begin( __METHOD__ ); + try { + $this->database->doAtomicSection( + __METHOD__, + function () { + throw new RuntimeException( 'Test exception' ); + } + ); + $this->fail( 'Test exception not thrown' ); + } catch ( RuntimeException $ex ) { + $this->assertSame( 'Test exception', $ex->getMessage() ); + } + $this->database->rollback( __METHOD__ ); + $this->assertLastSql( 'BEGIN; ROLLBACK' ); } public static function provideAtomicSectionMethodsForErrors() { @@ -1444,7 +1472,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { $this->fail( 'Expected exception not thrown' ); } catch ( DBUnexpectedError $ex ) { $this->assertSame( - 'No atomic transaction is open (got ' . __METHOD__ . ').', + 'No atomic section is open (got ' . __METHOD__ . ').', $ex->getMessage() ); } @@ -1462,7 +1490,8 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { $this->fail( 'Expected exception not thrown' ); } catch ( DBUnexpectedError $ex ) { $this->assertSame( - 'Invalid atomic section ended (got ' . __METHOD__ . ').', + 'Invalid atomic section ended (got ' . __METHOD__ . ' but expected ' . + __METHOD__ . 'X' . ').', $ex->getMessage() ); } @@ -1475,10 +1504,11 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { $this->database->startAtomic( __METHOD__ ); try { $this->database->cancelAtomic( __METHOD__ ); + $this->database->select( 'test', '1', [], __METHOD__ ); $this->fail( 'Expected exception not thrown' ); - } catch ( DBUnexpectedError $ex ) { + } catch ( DBTransactionError $ex ) { $this->assertSame( - 'Uncancelable atomic section canceled (got ' . __METHOD__ . ').', + 'Cannot execute query from ' . __METHOD__ . ' while transaction status is ERROR.', $ex->getMessage() ); } @@ -1545,7 +1575,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { * @covers \Wikimedia\Rdbms\Database::query */ public function testImplicitTransactionRollback() { - $doError = function ( $wasKnown = true ) { + $doError = function () { $this->database->forceNextQueryError( 666, 'Evilness' ); try { $this->database->delete( 'error', '1', __CLASS__ . '::SomeCaller' ); @@ -1559,7 +1589,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { // Implicit transaction gets silently rolled back $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL ); - call_user_func( $doError, false ); + call_user_func( $doError ); $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ ); $this->database->commit( __METHOD__, Database::FLUSHING_INTERNAL ); // phpcs:ignore @@ -1568,7 +1598,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { // ... unless there were prior writes $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL ); $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ ); - call_user_func( $doError, false ); + call_user_func( $doError ); try { $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ ); $this->fail( 'Expected exception not thrown' ); @@ -1579,6 +1609,71 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'1\'; DELETE FROM error WHERE 1; ROLLBACK' ); } + /** + * @covers \Wikimedia\Rdbms\Database::query + */ + public function testTransactionStatementRollbackIgnoring() { + $wrapper = TestingAccessWrapper::newFromObject( $this->database ); + $warning = []; + $wrapper->deprecationLogger = function ( $msg ) use ( &$warning ) { + $warning[] = $msg; + }; + + $doError = function () { + $this->database->forceNextQueryError( 666, 'Evilness', [ + 'wasKnownStatementRollbackError' => true, + ] ); + try { + $this->database->delete( 'error', '1', __CLASS__ . '::SomeCaller' ); + $this->fail( 'Expected exception not thrown' ); + } catch ( DBError $e ) { + $this->assertSame( 666, $e->errno ); + } + }; + $expectWarning = 'Caller from ' . __METHOD__ . + ' ignored an error originally raised from ' . __CLASS__ . '::SomeCaller: [666] Evilness'; + + // Rollback doesn't raise a warning + $warning = []; + $this->database->startAtomic( __METHOD__ ); + call_user_func( $doError ); + $this->database->rollback( __METHOD__ ); + $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ ); + $this->assertSame( [], $warning ); + // phpcs:ignore + $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; ROLLBACK; DELETE FROM x WHERE field = \'1\'' ); + + // cancelAtomic() doesn't raise a warning + $warning = []; + $this->database->begin( __METHOD__ ); + $this->database->startAtomic( __METHOD__, Database::ATOMIC_CANCELABLE ); + call_user_func( $doError ); + $this->database->cancelAtomic( __METHOD__ ); + $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ ); + $this->database->commit( __METHOD__ ); + $this->assertSame( [], $warning ); + // phpcs:ignore + $this->assertLastSql( 'BEGIN; SAVEPOINT wikimedia_rdbms_atomic1; DELETE FROM error WHERE 1; ROLLBACK TO SAVEPOINT wikimedia_rdbms_atomic1; DELETE FROM x WHERE field = \'1\'; COMMIT' ); + + // Commit does raise a warning + $warning = []; + $this->database->begin( __METHOD__ ); + call_user_func( $doError ); + $this->database->commit( __METHOD__ ); + $this->assertSame( [ $expectWarning ], $warning ); + $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; COMMIT' ); + + // Deprecation only gets raised once + $warning = []; + $this->database->begin( __METHOD__ ); + call_user_func( $doError ); + $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ ); + $this->database->commit( __METHOD__ ); + $this->assertSame( [ $expectWarning ], $warning ); + // phpcs:ignore + $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; DELETE FROM x WHERE field = \'1\'; COMMIT' ); + } + /** * @covers \Wikimedia\Rdbms\Database::close */ @@ -1621,4 +1716,42 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'3\'; ROLLBACK' ); $this->assertEquals( 0, $this->database->trxLevel() ); } + + /** + * @covers \Wikimedia\Rdbms\Database::close + */ + public function testPrematureClose3() { + try { + $this->database->setFlag( IDatabase::DBO_TRX ); + $this->database->delete( 'x', [ 'field' => 3 ], __METHOD__ ); + $this->assertEquals( 1, $this->database->trxLevel() ); + $this->database->close(); + $this->fail( 'Expected exception not thrown' ); + } catch ( DBUnexpectedError $ex ) { + $this->assertSame( + 'Wikimedia\Rdbms\Database::close: ' . + 'mass commit/rollback of peer transaction required (DBO_TRX set).', + $ex->getMessage() + ); + } + + $this->assertFalse( $this->database->isOpen() ); + $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'3\'; ROLLBACK' ); + $this->assertEquals( 0, $this->database->trxLevel() ); + } + + /** + * @covers \Wikimedia\Rdbms\Database::close + */ + public function testPrematureClose4() { + $this->database->setFlag( IDatabase::DBO_TRX ); + $this->database->query( 'SELECT 1', __METHOD__ ); + $this->assertEquals( 1, $this->database->trxLevel() ); + $this->database->close(); + $this->database->clearFlag( IDatabase::DBO_TRX ); + + $this->assertFalse( $this->database->isOpen() ); + $this->assertLastSql( 'BEGIN; SELECT 1; COMMIT' ); + $this->assertEquals( 0, $this->database->trxLevel() ); + } }